Browse Source

Add lightweight profiling (#10961)

Also remove most of the timing information that was previously logged

Change-Id: I8269036a12762eb63f7d4f93aefb6be307dd620a
tags/7.0.1
Leif Åstrand 11 years ago
parent
commit
34096d1784

+ 9
- 0
client/src/com/vaadin/Vaadin.gwt.xml View File

@@ -38,6 +38,15 @@
<when-type-assignable
class="com.vaadin.client.metadata.ConnectorBundleLoader" />
</generate-with>
<!-- Set vaadin.profiler to true to include profiling support in the module -->
<define-property name="vaadin.profiler" values="true,false" />
<set-property name="vaadin.profiler" value="false" />
<replace-with class="com.vaadin.client.Profiler.EnabledProfiler">
<when-type-is class="com.vaadin.client.Profiler" />
<when-property-is name="vaadin.profiler" value="true" />
</replace-with>

<!-- Use the new cross site linker to get a nocache.js without document.write -->
<add-linker name="xsiframe" />

+ 96
- 52
client/src/com/vaadin/client/ApplicationConnection.java View File

@@ -1311,6 +1311,8 @@ public class ApplicationConnection {
return;
}

Profiler.reset();

VConsole.log("Handling message from server");
eventBus.fireEvent(new ResponseHandlingStartedEvent(this));

@@ -1384,8 +1386,7 @@ public class ApplicationConnection {
handleUIDLDuration.logDuration(" * Loading widgets completed",
10);

MultiStepDuration updateDuration = new MultiStepDuration();

Profiler.enter("Handling locales");
if (json.containsKey("locales")) {
VConsole.log(" * Handling locales");
// Store locale data
@@ -1393,9 +1394,9 @@ public class ApplicationConnection {
.getJSValueMapArray("locales");
LocaleService.addLocales(valueMapArray);
}
Profiler.leave("Handling locales");

updateDuration.logDuration(" * Handling locales completed", 10);

Profiler.enter("Handling meta information");
boolean repaintAll = false;
ValueMap meta = null;
if (json.containsKey("meta")) {
@@ -1422,9 +1423,7 @@ public class ApplicationConnection {
.getInt("interval");
}
}

updateDuration.logDuration(
" * Handling meta information completed", 10);
Profiler.leave("Handling meta information");

if (redirectTimer != null) {
redirectTimer.schedule(1000 * sessionExpirationInterval);
@@ -1432,34 +1431,21 @@ public class ApplicationConnection {

componentCaptionSizeChanges.clear();

int startProcessing = updateDuration.elapsedMillis();
double processUidlStart = Duration.currentTimeMillis();

// Ensure that all connectors that we are about to update exist
Set<ServerConnector> createdConnectors = createConnectorsIfNeeded(json);

updateDuration.logDuration(" * Creating connectors completed",
10);

// Update states, do not fire events
Collection<StateChangeEvent> pendingStateChangeEvents = updateConnectorState(
json, createdConnectors);

updateDuration.logDuration(
" * Update of connector states completed", 10);

// Update hierarchy, do not fire events
ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(json);

updateDuration.logDuration(
" * Update of connector hierarchy completed", 10);

// Fire hierarchy change events
sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events);

updateDuration.logDuration(
" * Hierarchy state change event processing completed",
10);

updateCaptions(pendingStateChangeEvents,
connectorHierarchyUpdateResult.parentChanged);

@@ -1468,43 +1454,25 @@ public class ApplicationConnection {
// Fire state change events.
sendStateChangeEvents(pendingStateChangeEvents);

updateDuration.logDuration(
" * State change event processing completed", 10);

// Update of legacy (UIDL) style connectors
updateVaadin6StyleConnectors(json);

updateDuration
.logDuration(
" * Vaadin 6 style connector updates (updateFromUidl) completed",
10);

// Handle any RPC invocations done on the server side
handleRpcInvocations(json);

updateDuration.logDuration(
" * Processing of RPC invocations completed", 10);

if (json.containsKey("dd")) {
// response contains data for drag and drop service
VDragAndDropManager.get().handleServerResponse(
json.getValueMap("dd"));
}

updateDuration
.logDuration(
" * Processing of drag and drop server response completed",
10);

unregisterRemovedConnectors();

updateDuration.logDuration(
" * Unregistering of removed components completed", 10);

VConsole.log("handleUIDLMessage: "
+ (updateDuration.elapsedMillis() - startProcessing)
+ (Duration.currentTimeMillis() - processUidlStart)
+ " ms");

Profiler.enter("Layout processing");
try {
LayoutManager layoutManager = getLayoutManager();
layoutManager.setEverythingNeedsMeasure();
@@ -1512,21 +1480,17 @@ public class ApplicationConnection {
} catch (final Throwable e) {
VConsole.error(e);
}

updateDuration
.logDuration(" * Layout processing completed", 10);
Profiler.leave("Layout processing");

if (ApplicationConfiguration.isDebugMode()) {
Profiler.enter("Dumping state changes to the console");
VConsole.log(" * Dumping state changes to the console");
VConsole.dirUIDL(json, ApplicationConnection.this);

updateDuration
.logDuration(
" * Dumping state changes to the console completed",
10);
Profiler.leave("Dumping state changes to the console");
}

if (meta != null) {
Profiler.enter("Error handling");
if (meta.containsKey("appError")) {
ValueMap error = meta.getValueMap("appError");

@@ -1547,10 +1511,9 @@ public class ApplicationConnection {
validatingLayouts = false;

}
Profiler.leave("Error handling");
}

updateDuration.logDuration(" * Error handling completed", 10);

// TODO build profiling for widget impl loading time

lastProcessingTime = (int) ((new Date().getTime()) - start
@@ -1564,11 +1527,18 @@ public class ApplicationConnection {

endRequest();

if (Profiler.isEnabled()) {
Profiler.logTimings();
Profiler.reset();
}

}

private void updateCaptions(
Collection<StateChangeEvent> pendingStateChangeEvents,
Collection<ServerConnector> parentChanged) {
Profiler.enter("updateCaptions");

/*
* Find all components that might need a caption update based on
* pending state and hierarchy changes
@@ -1589,15 +1559,21 @@ public class ApplicationConnection {
.delegateCaptionHandling()) {
ServerConnector parent = child.getParent();
if (parent instanceof HasComponentsConnector) {
Profiler.enter("HasComponentsConnector.updateCaption");
((HasComponentsConnector) parent)
.updateCaption((ComponentConnector) child);
Profiler.leave("HasComponentsConnector.updateCaption");
}
}
}

Profiler.leave("updateCaptions");
}

private void delegateToWidget(
Collection<StateChangeEvent> pendingStateChangeEvents) {
Profiler.enter("@DelegateToWidget");

VConsole.log(" * Running @DelegateToWidget");

for (StateChangeEvent sce : pendingStateChangeEvents) {
@@ -1618,12 +1594,16 @@ public class ApplicationConnection {
String method = property
.getDelegateToWidgetMethodName();
if (method != null) {
Profiler.enter("doDelegateToWidget");
doDelegateToWidget(component, property, method);
Profiler.leave("doDelegateToWidget");
}
}

}
}

Profiler.leave("@DelegateToWidget");
}

private void doDelegateToWidget(ComponentConnector component,
@@ -1661,6 +1641,7 @@ public class ApplicationConnection {
*/
private void sendStateChangeEvents(
Collection<StateChangeEvent> pendingStateChangeEvents) {
Profiler.enter("sendStateChangeEvents");
VConsole.log(" * Sending state change events");

for (StateChangeEvent sce : pendingStateChangeEvents) {
@@ -1671,9 +1652,12 @@ public class ApplicationConnection {
}
}

Profiler.leave("sendStateChangeEvents");
}

private void unregisterRemovedConnectors() {
Profiler.enter("unregisterRemovedConnectors");

int unregistered = 0;
JsArrayObject<ServerConnector> currentConnectors = connectorMap
.getConnectorsAsJsArray();
@@ -1703,6 +1687,7 @@ public class ApplicationConnection {
}

VConsole.log("* Unregistered " + unregistered + " connectors");
Profiler.leave("unregisterRemovedConnectors");
}

private Set<ServerConnector> createConnectorsIfNeeded(ValueMap json) {
@@ -1712,6 +1697,8 @@ public class ApplicationConnection {
return Collections.emptySet();
}

Profiler.enter("Creating connectors");

Set<ServerConnector> createdConnectors = new HashSet<ServerConnector>();

ValueMap types = json.getValueMap("types");
@@ -1733,7 +1720,10 @@ public class ApplicationConnection {
// Connector does not exist so we must create it
if (connectorClass != UIConnector.class) {
// create, initialize and register the paintable
Profiler.enter("ApplicationConnection.getConnector");
connector = getConnector(connectorId, connectorType);
Profiler.leave("ApplicationConnection.getConnector");

createdConnectors.add(connector);
} else {
// First UIConnector update. Before this the
@@ -1750,10 +1740,15 @@ public class ApplicationConnection {
VConsole.error(e);
}
}

Profiler.leave("Creating connectors");

return createdConnectors;
}

private void updateVaadin6StyleConnectors(ValueMap json) {
Profiler.enter("updateVaadin6StyleConnectors");

JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
int length = changes.length();

@@ -1768,8 +1763,19 @@ public class ApplicationConnection {
final ComponentConnector legacyConnector = (ComponentConnector) connectorMap
.getConnector(connectorId);
if (legacyConnector instanceof Paintable) {
String key = null;
if (Profiler.isEnabled()) {
key = "updateFromUIDL for "
+ Util.getSimpleName(legacyConnector);
Profiler.enter(key);
}

((Paintable) legacyConnector).updateFromUIDL(uidl,
ApplicationConnection.this);

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} else if (legacyConnector == null) {
VConsole.error("Received update for "
+ uidl.getTag()
@@ -1785,6 +1791,8 @@ public class ApplicationConnection {
VConsole.error(e);
}
}

Profiler.leave("updateVaadin6StyleConnectors");
}

private void sendHierarchyChangeEvents(
@@ -1792,6 +1800,7 @@ public class ApplicationConnection {
if (pendingHierarchyChangeEvents.isEmpty()) {
return;
}
Profiler.enter("sendHierarchyChangeEvents");

VConsole.log(" * Sending hierarchy change events");
for (ConnectorHierarchyChangeEvent event : pendingHierarchyChangeEvents) {
@@ -1803,6 +1812,7 @@ public class ApplicationConnection {
}
}

Profiler.leave("sendHierarchyChangeEvents");
}

private void logHierarchyChange(ConnectorHierarchyChangeEvent event) {
@@ -1835,6 +1845,9 @@ public class ApplicationConnection {
if (!json.containsKey("state")) {
return events;
}

Profiler.enter("updateConnectorState");

HashSet<ServerConnector> remainingNewConnectors = new HashSet<ServerConnector>(
newConnectors);

@@ -1847,6 +1860,11 @@ public class ApplicationConnection {
ServerConnector connector = connectorMap
.getConnector(connectorId);
if (null != connector) {
Profiler.enter("updateConnectorState inner loop");
if (Profiler.isEnabled()) {
Profiler.enter("Decode connector state "
+ Util.getSimpleName(connector));
}

JSONObject stateJson = new JSONObject(
states.getJavaScriptObject(connectorId));
@@ -1859,10 +1877,19 @@ public class ApplicationConnection {
}

SharedState state = connector.getState();

Profiler.enter("updateConnectorState decodeValue");
JsonDecoder.decodeValue(new Type(state.getClass()
.getName(), null), stateJson, state,
ApplicationConnection.this);
Profiler.leave("updateConnectorState decodeValue");

if (Profiler.isEnabled()) {
Profiler.leave("Decode connector state "
+ Util.getSimpleName(connector));
}

Profiler.enter("updateConnectorState create event");
FastStringSet changedProperties = FastStringSet
.create();
addJsonFields(stateJson, changedProperties, "");
@@ -1880,6 +1907,9 @@ public class ApplicationConnection {
connector, changedProperties);

events.add(event);
Profiler.leave("updateConnectorState create event");

Profiler.leave("updateConnectorState inner loop");
}
} catch (final Throwable e) {
VConsole.error(e);
@@ -1899,6 +1929,8 @@ public class ApplicationConnection {

}

Profiler.leave("updateConnectorState");

return events;
}

@@ -1906,9 +1938,11 @@ public class ApplicationConnection {
FastStringSet fields;
fields = allStateFieldsCache.get(type.getBaseTypeName());
if (fields == null) {
Profiler.enter("getAllStateFields create");
fields = FastStringSet.create();
addAllStateFields(type, fields, "");
allStateFieldsCache.put(type.getBaseTypeName(), fields);
Profiler.leave("getAllStateFields create");
}
return fields;
}
@@ -1994,6 +2028,8 @@ public class ApplicationConnection {
return result;
}

Profiler.enter("updateConnectorHierarchy");

FastStringSet maybeDetached = FastStringSet.create();

ValueMap hierarchies = json.getValueMap("hierarchy");
@@ -2113,6 +2149,8 @@ public class ApplicationConnection {
recursivelyDetach(removed, result.events);
}

Profiler.leave("updateConnectorHierarchy");

return result;

}
@@ -2200,6 +2238,8 @@ public class ApplicationConnection {

private void handleRpcInvocations(ValueMap json) {
if (json.containsKey("rpc")) {
Profiler.enter("handleRpcInvocations");

VConsole.log(" * Performing server to client RPC calls");

JSONArray rpcCalls = new JSONArray(
@@ -2215,8 +2255,9 @@ public class ApplicationConnection {
VConsole.error(e);
}
}
}

Profiler.leave("handleRpcInvocations");
}
}

};
@@ -2864,12 +2905,15 @@ public class ApplicationConnection {
*/
private ServerConnector createAndRegisterConnector(String connectorId,
int connectorType) {
Profiler.enter("ApplicationConnection.createAndRegisterConnector");

// Create and register a new connector with the given type
ServerConnector p = widgetSet.createConnector(connectorType,
configuration);
connectorMap.registerConnector(connectorId, p);
p.doInit(connectorId, this);

Profiler.leave("ApplicationConnection.createAndRegisterConnector");
return p;
}


+ 4
- 0
client/src/com/vaadin/client/ConnectorMap.java View File

@@ -120,13 +120,17 @@ public class ConnectorMap {
}

public void registerConnector(String id, ServerConnector connector) {
Profiler.enter("ConnectorMap.registerConnector");
ComponentDetail componentDetail = GWT.create(ComponentDetail.class);
idToComponentDetail.put(id, componentDetail);
componentDetail.setConnector(connector);
if (connector instanceof ComponentConnector) {
ComponentConnector pw = (ComponentConnector) connector;
Profiler.enter("ConnectorMap.setConnectorId");
setConnectorId(pw.getWidget().getElement(), id);
Profiler.leave("ConnectorMap.setConnectorId");
}
Profiler.leave("ConnectorMap.registerConnector");
}

private static native void setConnectorId(Element el, String id)

+ 118
- 25
client/src/com/vaadin/client/LayoutManager.java View File

@@ -259,6 +259,7 @@ public class LayoutManager {

private void doLayout() {
VConsole.log("Starting layout phase");
Profiler.enter("LayoutManager phase init");

FastStringMap<Integer> layoutCounts = FastStringMap.create();

@@ -293,27 +294,30 @@ public class LayoutManager {

measureNonConnectors();

VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms");
Profiler.leave("LayoutManager phase init");

while (true) {
Duration passDuration = new Duration();
Profiler.enter("Layout pass");
passes++;

performBrowserLayoutHacks();

Profiler.enter("Layout measure connectors");
int measuredConnectorCount = measureConnectors(
currentDependencyTree, everythingNeedsMeasure);
Profiler.leave("Layout measure connectors");

everythingNeedsMeasure = false;
if (measuredConnectorCount == 0) {
VConsole.log("No more changes in pass " + passes);
Profiler.leave("Layout pass");
break;
}

int measureTime = passDuration.elapsedMillis();
VConsole.log(" Measured " + measuredConnectorCount
+ " elements in " + measureTime + " ms");

int firedListeners = 0;
if (!listenersToFire.isEmpty()) {
firedListeners = listenersToFire.size();
Profiler.enter("Layout fire resize events");
for (Element element : listenersToFire) {
Collection<ElementResizeListener> listeners = elementResizeListeners
.get(element);
@@ -325,23 +329,33 @@ public class LayoutManager {
element);
for (ElementResizeListener listener : array) {
try {
String key = null;
if (Profiler.isEnabled()) {
key = "ElementReizeListener.onElementReize for "
+ Util.getSimpleName(listener);
Profiler.enter(key);
}

listener.onElementResize(event);
if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} catch (RuntimeException e) {
VConsole.error(e);
}
}
}
}
int measureListenerTime = passDuration.elapsedMillis();
VConsole.log(" Fired resize listeners for "
+ listenersToFire.size() + " elements in "
+ (measureListenerTime - measureTime) + " ms");
measureTime = measuredConnectorCount;
listenersToFire.clear();

Profiler.leave("Layout fire resize events");
}

Profiler.enter("LayoutManager handle ManagedLayout");

FastStringSet updatedSet = FastStringSet.create();

int layoutCount = 0;
while (currentDependencyTree.hasHorizontalConnectorToLayout()
|| currentDependencyTree.hasVerticaConnectorToLayout()) {

@@ -356,7 +370,19 @@ public class LayoutManager {
.markAsHorizontallyLayouted(layout);
DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
try {
String key = null;
if (Profiler.isEnabled()) {
key = "layoutHorizontally() for "
+ Util.getSimpleName(cl);
Profiler.enter(key);
}

cl.layoutHorizontally();
layoutCount++;

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} catch (RuntimeException e) {
VConsole.error(e);
}
@@ -367,7 +393,18 @@ public class LayoutManager {
currentDependencyTree.markAsVerticallyLayouted(layout);
SimpleManagedLayout rr = (SimpleManagedLayout) layout;
try {
String key = null;
if (Profiler.isEnabled()) {
key = "layout() for " + Util.getSimpleName(rr);
Profiler.enter(key);
}

rr.layout();
layoutCount++;

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} catch (RuntimeException e) {
VConsole.error(e);
}
@@ -388,7 +425,19 @@ public class LayoutManager {
currentDependencyTree.markAsVerticallyLayouted(layout);
DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
try {
String key = null;
if (Profiler.isEnabled()) {
key = "layoutHorizontally() for "
+ Util.getSimpleName(cl);
Profiler.enter(key);
}

cl.layoutVertically();
layoutCount++;

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} catch (RuntimeException e) {
VConsole.error(e);
}
@@ -399,7 +448,18 @@ public class LayoutManager {
currentDependencyTree.markAsVerticallyLayouted(layout);
SimpleManagedLayout rr = (SimpleManagedLayout) layout;
try {
String key = null;
if (Profiler.isEnabled()) {
key = "layout() for " + Util.getSimpleName(rr);
Profiler.enter(key);
}

rr.layout();
layoutCount++;

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
} catch (RuntimeException e) {
VConsole.error(e);
}
@@ -411,14 +471,14 @@ public class LayoutManager {
}
}

Profiler.leave("LayoutManager handle ManagedLayout");

if (debugLogging) {
JsArrayString changedCids = updatedSet.dump();

StringBuilder b = new StringBuilder(" ");
b.append(changedCids.length());
b.append(" requestLayout invocations in ");
b.append(passDuration.elapsedMillis() - measureTime);
b.append(" ms");
b.append(" requestLayout invocations ");
if (changedCids.length() < 30) {
for (int i = 0; i < changedCids.length(); i++) {
if (i != 0) {
@@ -439,8 +499,12 @@ public class LayoutManager {
VConsole.log(b.toString());
}

VConsole.log("Pass " + passes + " completed in "
+ passDuration.elapsedMillis() + " ms");
Profiler.leave("Layout pass");

VConsole.log("Pass " + passes + " measured "
+ measuredConnectorCount + " elements, fired "
+ firedListeners + " listeners and did " + layoutCount
+ " layouts.");

if (passes > 100) {
VConsole.log(LOOP_ABORT_MESSAGE);
@@ -455,23 +519,30 @@ public class LayoutManager {
}
}

int postLayoutStart = totalDuration.elapsedMillis();
Profiler.enter("layout PostLayoutListener");
JsArrayObject<ComponentConnector> componentConnectors = connectorMap
.getComponentConnectorsAsJsArray();
int size = componentConnectors.size();
for (int i = 0; i < size; i++) {
ComponentConnector connector = componentConnectors.get(i);
if (connector instanceof PostLayoutListener) {
String key = null;
if (Profiler.isEnabled()) {
key = "layout PostLayoutListener for "
+ Util.getSimpleName(connector);
Profiler.enter(key);
}

((PostLayoutListener) connector).postLayout();

if (Profiler.isEnabled()) {
Profiler.leave(key);
}
}
}
int postLayoutDone = totalDuration.elapsedMillis();
VConsole.log("Invoke post layout listeners in "
+ (postLayoutDone - postLayoutStart) + " ms");
Profiler.leave("layout PostLayoutListener");

cleanMeasuredSizes();
int cleaningTime = (totalDuration.elapsedMillis() - postLayoutDone);
VConsole.log("Cleaned old measured sizes in " + cleaningTime + "ms");

VConsole.log("Total layout phase time: "
+ totalDuration.elapsedMillis() + "ms");
@@ -485,13 +556,12 @@ public class LayoutManager {

private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
boolean measureAll) {
Profiler.enter("Layout overflow fix handling");
JsArrayString pendingOverflowConnectorsIds = pendingOverflowFixes
.dump();
int pendingOverflowCount = pendingOverflowConnectorsIds.length();
ConnectorMap connectorMap = ConnectorMap.get(connection);
if (pendingOverflowCount > 0) {
Duration duration = new Duration();

HashMap<Element, String> originalOverflows = new HashMap<Element, String>();

FastStringSet delayedOverflowFixes = FastStringSet.create();
@@ -523,6 +593,7 @@ public class LayoutManager {
+ Util.getConnectorString(componentConnector
.getParent()));
}
Profiler.enter("Overflow fix apply");

Element parentElement = componentConnector.getWidget()
.getElement().getParentElement();
@@ -537,6 +608,7 @@ public class LayoutManager {
}

style.setOverflow(Overflow.HIDDEN);
Profiler.leave("Overflow fix apply");
}

pendingOverflowFixes.removeAll(delayedOverflowFixes);
@@ -544,6 +616,7 @@ public class LayoutManager {
JsArrayString remainingOverflowFixIds = pendingOverflowFixes.dump();
int remainingCount = remainingOverflowFixIds.length();

Profiler.enter("Overflow fix reflow");
// Then ensure all scrolling elements are reflowed by measuring
for (int i = 0; i < remainingCount; i++) {
ComponentConnector componentConnector = (ComponentConnector) connectorMap
@@ -551,7 +624,9 @@ public class LayoutManager {
componentConnector.getWidget().getElement().getParentElement()
.getOffsetHeight();
}
Profiler.leave("Overflow fix reflow");

Profiler.enter("Overflow fix restore");
// Finally restore old overflow value and update bookkeeping
for (int i = 0; i < remainingCount; i++) {
String connectorId = remainingOverflowFixIds.get(i);
@@ -564,15 +639,19 @@ public class LayoutManager {

layoutDependencyTree.setNeedsMeasure(connectorId, true);
}
Profiler.leave("Overflow fix restore");

if (!pendingOverflowFixes.isEmpty()) {
VConsole.log("Did overflow fix for " + remainingCount
+ " elements in " + duration.elapsedMillis() + " ms");
+ " elements");
}
pendingOverflowFixes = delayedOverflowFixes;
}
Profiler.leave("Layout overflow fix handling");

int measureCount = 0;
if (measureAll) {
Profiler.enter("Layout measureAll");
JsArrayObject<ComponentConnector> allConnectors = connectorMap
.getComponentConnectorsAsJsArray();
int size = allConnectors.size();
@@ -596,8 +675,11 @@ public class LayoutManager {
.getConnectorId(), false);
}
measureCount += connectorCount;

Profiler.leave("Layout measureAll");
}

Profiler.enter("Layout measure from tree");
while (layoutDependencyTree.hasConnectorsToMeasure()) {
JsArrayString measureTargets = layoutDependencyTree
.getMeasureTargetsJsArray();
@@ -613,10 +695,13 @@ public class LayoutManager {
layoutDependencyTree.setNeedsMeasure(connectorId, false);
}
}
Profiler.leave("Layout measure from tree");

return measureCount;
}

private void measureConnector(ComponentConnector connector) {
Profiler.enter("LayoutManager.measureConnector");
Element element = connector.getWidget().getElement();
MeasuredSize measuredSize = getMeasuredSize(connector);
MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
@@ -625,10 +710,12 @@ public class LayoutManager {
onConnectorChange(connector, measureResult.isWidthChanged(),
measureResult.isHeightChanged());
}
Profiler.leave("LayoutManager.measureConnector");
}

private void onConnectorChange(ComponentConnector connector,
boolean widthChanged, boolean heightChanged) {
Profiler.enter("LayoutManager.onConnectorChange");
setNeedsOverflowFix(connector);
if (heightChanged) {
currentDependencyTree.markHeightAsChanged(connector);
@@ -636,6 +723,7 @@ public class LayoutManager {
if (widthChanged) {
currentDependencyTree.markWidthAsChanged(connector);
}
Profiler.leave("LayoutManager.onConnectorChange");
}

private void setNeedsOverflowFix(ComponentConnector connector) {
@@ -651,9 +739,11 @@ public class LayoutManager {
}

private void measureNonConnectors() {
Profiler.enter("LayoutManager.measureNonConenctors");
for (Element element : measuredNonConnectorElements) {
measuredAndUpdate(element, getMeasuredSize(element, null));
}
Profiler.leave("LayoutManager.measureNonConenctors");
VConsole.log("Measured " + measuredNonConnectorElements.size()
+ " non connector elements");
}
@@ -673,6 +763,8 @@ public class LayoutManager {
boolean widthChanged, boolean heightChanged) {
assert widthChanged || heightChanged;

Profiler.enter("LayoutManager.notifyListenersAndDepdendents");

MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
JsArrayString dependents = measuredSize.getDependents();
for (int i = 0; i < dependents.length(); i++) {
@@ -689,6 +781,7 @@ public class LayoutManager {
if (elementResizeListeners.containsKey(element)) {
listenersToFire.add(element);
}
Profiler.leave("LayoutManager.notifyListenersAndDepdendents");
}

private static boolean isManagedLayout(ComponentConnector connector) {

+ 5
- 0
client/src/com/vaadin/client/LayoutManagerIE8.java View File

@@ -61,6 +61,7 @@ public class LayoutManagerIE8 extends LayoutManager {

@Override
protected void cleanMeasuredSizes() {
Profiler.enter("LayoutManager.cleanMeasuredSizes");
Document document = RootPanel.get().getElement().getOwnerDocument();

Iterator<Element> i = measuredSizes.keySet().iterator();
@@ -70,15 +71,19 @@ public class LayoutManagerIE8 extends LayoutManager {
i.remove();
}
}

Profiler.leave("LayoutManager.cleanMeasuredSizes");
}

@Override
protected void performBrowserLayoutHacks() {
Profiler.enter("LayoutManagerIE8.performBrowserLayoutHacks");
/*
* Fixes IE8 issues where IE8 sometimes forgets to update the size of
* the containing element. To force a reflow by modifying the magical
* zoom property.
*/
Util.forceIE8Redraw(RootPanel.get().getElement());
Profiler.leave("LayoutManagerIE8.performBrowserLayoutHacks");
}
}

+ 18
- 0
client/src/com/vaadin/client/MeasuredSize.java View File

@@ -185,11 +185,18 @@ public class MeasuredSize {
}

public MeasureResult measure(Element element) {
Profiler.enter("MeasuredSize.measure");
boolean heightChanged = false;
boolean widthChanged = false;

Profiler.enter("new ComputedStyle");
ComputedStyle computedStyle = new ComputedStyle(element);
int[] paddings = computedStyle.getPadding();
// Some browsers do not reflow until accessing data from the computed
// style object
Profiler.leave("new ComputedStyle");

Profiler.enter("Measure paddings");
if (!heightChanged && hasHeightChanged(this.paddings, paddings)) {
debugSizeChange(element, "Height (padding)", this.paddings,
paddings);
@@ -200,7 +207,9 @@ public class MeasuredSize {
widthChanged = true;
}
this.paddings = paddings;
Profiler.leave("Measure paddings");

Profiler.enter("Measure margins");
int[] margins = computedStyle.getMargin();
if (!heightChanged && hasHeightChanged(this.margins, margins)) {
debugSizeChange(element, "Height (margins)", this.margins, margins);
@@ -211,7 +220,9 @@ public class MeasuredSize {
widthChanged = true;
}
this.margins = margins;
Profiler.leave("Measure margins");

Profiler.enter("Measure borders");
int[] borders = computedStyle.getBorder();
if (!heightChanged && hasHeightChanged(this.borders, borders)) {
debugSizeChange(element, "Height (borders)", this.borders, borders);
@@ -222,7 +233,9 @@ public class MeasuredSize {
widthChanged = true;
}
this.borders = borders;
Profiler.leave("Measure borders");

Profiler.enter("Measure height");
int requiredHeight = Util.getRequiredHeight(element);
int marginHeight = sumHeights(margins);
int oldHeight = height;
@@ -231,13 +244,18 @@ public class MeasuredSize {
debugSizeChange(element, "Height (outer)", oldHeight, height);
heightChanged = true;
}
Profiler.leave("Measure height");

Profiler.enter("Measure width");
int requiredWidth = Util.getRequiredWidth(element);
int marginWidth = sumWidths(margins);
if (setOuterWidth(requiredWidth + marginWidth)) {
debugSizeChange(element, "Width (outer)", oldWidth, width);
widthChanged = true;
}
Profiler.leave("Measure width");

Profiler.leave("MeasuredSize.measure");

return new MeasureResult(widthChanged, heightChanged);
}

+ 365
- 0
client/src/com/vaadin/client/Profiler.java View File

@@ -0,0 +1,365 @@
/*
* Copyright 2012 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.client;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;

import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

/**
* Lightweight profiling tool that can be used to collect profiling data with
* zero overhead unless enabled. To enable profiling, add
* <code>&lt;set-property name="vaadin.profiler" value="true" /&gt;</code> to
* your .gwt.xml file.
*
* @author Vaadin Ltd
* @since 7.0.0
*/
public class Profiler {
/**
* Class to include using deferred binding to enable the profiling.
*
* @author Vaadin Ltd
* @since 7.0.0
*/
public static class EnabledProfiler extends Profiler {
@Override
protected boolean isImplEnabled() {
return true;
}
}

private static JsArray<ProfilerEvent> events;

private static final class ProfilerEvent extends JavaScriptObject {
protected ProfilerEvent() {
// JSO constructor
}

public native String getName()
/*-{
return this.name;
}-*/;

private native double getRawTime()
/*-{
return this.time;
}-*/;

private boolean isStart() {
return getRawTime() <= 0;
}
}

private static class Node {

private final String name;
private final LinkedHashMap<String, Node> children = new LinkedHashMap<String, Node>();
private double time = 0;
private int count = 0;

public Node(String name) {
this.name = name;
}

public String getName() {
return name;
}

public Node addEvent(ProfilerEvent event) {
Node child = children.get(event.getName());
if (child == null) {
child = new Node(event.getName());
children.put(event.getName(), child);
}
child.time += event.getRawTime();
child.count++;
return child;
}

public void registerEnd(ProfilerEvent event) {
time += event.getRawTime();
}

public double getTimeSpent() {
return time;
}

public int getCount() {
return count;
}

public double getOwnTime() {
double time = getTimeSpent();
for (Node node : children.values()) {
time -= node.getTimeSpent();
}
return time;
}

public Widget buildTree() {
String message = getStringRepresentation("");

if (getName() == null || !children.isEmpty()) {
SimpleTree tree = new SimpleTree(message);
for (Node node : children.values()) {
Widget child = node.buildTree();
tree.add(child);
}
return tree;
} else {
return new Label(message);
}
}

public void buildRecursiveString(StringBuilder builder, String prefix) {
if (getName() != null) {
String msg = getStringRepresentation(prefix);
builder.append(msg + '\n');
}
String childPrefix = prefix + "*";
for (Node node : children.values()) {
node.buildRecursiveString(builder, childPrefix);
}
}

private String getStringRepresentation(String prefix) {
if (getName() == null) {
return "";
}
String msg = prefix + " " + getName() + " in " + getTimeSpent()
+ " ms.";
if (getCount() > 1) {
msg += " Invoked "
+ getCount()
+ " times ("
+ roundToSignificantFigures(getTimeSpent() / getCount())
+ " ms per time).";
}
if (!children.isEmpty()) {
double ownTime = getOwnTime();
msg += " " + ownTime + " ms spent in own code";
if (getCount() > 1) {
msg += " ("
+ roundToSignificantFigures(ownTime / getCount())
+ " ms per time)";
}
msg += '.';
}
return msg;
}

public static double roundToSignificantFigures(double num) {
// Number of significant digits
int n = 3;
if (num == 0) {
return 0;
}

final double d = Math.ceil(Math.log10(num < 0 ? -num : num));
final int power = n - (int) d;

final double magnitude = Math.pow(10, power);
final long shifted = Math.round(num * magnitude);
return shifted / magnitude;
}

public void sumUpTotals(Map<String, Node> totals) {
String name = getName();
if (name != null) {
Node totalNode = totals.get(name);
if (totalNode == null) {
totalNode = new Node(name);
totals.put(name, totalNode);
}

totalNode.time += getOwnTime();
totalNode.count += getCount();
}
for (Node node : children.values()) {
node.sumUpTotals(totals);
}
}
}

/**
* Checks whether the profiling gathering is enabled.
*
* @return <code>true</code> if the profiling is enabled, else
* <code>false</code>
*/
public static boolean isEnabled() {
// This will be fully inlined by the compiler
Profiler create = GWT.create(Profiler.class);
return create.isImplEnabled();
}

/**
* Enters a named block. There should always be a matching invocation of
* {@link #leave(String)} when leaving the block. Calls to this method will
* be removed by the compiler unless profiling is enabled.
*
* @param name
* the name of the entered block
*/
public static void enter(String name) {
if (isEnabled()) {
pushEvent(events, name, -Duration.currentTimeMillis());
}
}

/**
* Leaves a named block. There should always be a matching invocation of
* {@link #enter(String)} when entering the block. Calls to this method will
* be removed by the compiler unless profiling is enabled.
*
* @param name
* the name of the left block
*/
public static void leave(String name) {
if (isEnabled()) {
pushEvent(events, name, Duration.currentTimeMillis());
}
}

private static native final void pushEvent(JsArray<ProfilerEvent> target,
String name, double time)
/*-{
target[target.length] = {name: name, time: time};
}-*/;

/**
* Resets the collected profiler data. Calls to this method will be removed
* by the compiler unless profiling is enabled.
*/
public static void reset() {
if (isEnabled()) {
events = JavaScriptObject.createArray().cast();
}
}

/**
* Outputs the gathered profiling data to the debug console.
*/
public static void logTimings() {
if (!isEnabled()) {
VConsole.log("Profiler is not enabled, no data has been collected.");
return;
}

LinkedList<Node> stack = new LinkedList<Node>();
Node rootNode = new Node(null);
stack.add(rootNode);
for (int i = 0; i < events.length(); i++) {
ProfilerEvent event = events.get(i);
if (event.isStart()) {
Node stackTop = stack.getLast().addEvent(event);
stack.add(stackTop);
} else {
Node stackTop = stack.removeLast();
if (stackTop == null) {
VConsole.error("Leaving " + event.getName()
+ " that was never entered.");
return;
}
if (!stackTop.getName().equals(event.getName())) {
VConsole.error("Invalid profiling event order, leaving "
+ event.getName() + " but " + stackTop.getName()
+ " was expected");
return;
}
stackTop.registerEnd(event);
}
}

if (stack.size() != 1) {
VConsole.log("Not all nodes are left, the last node is "
+ stack.getLast().getName());
return;
}

StringBuilder stringBuilder = new StringBuilder();
rootNode.buildRecursiveString(stringBuilder, "");
Console implementation = VConsole.getImplementation();
if (implementation instanceof VDebugConsole) {
VDebugConsole console = (VDebugConsole) implementation;

SimpleTree tree = (SimpleTree) stack.getFirst().buildTree();
tree.setText("Profiler data");

console.showTree(tree, stringBuilder.toString());
} else {
VConsole.log(stringBuilder.toString());
}

Map<String, Node> totals = new HashMap<String, Node>();
rootNode.sumUpTotals(totals);

ArrayList<Node> totalList = new ArrayList<Node>(totals.values());
Collections.sort(totalList, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return (int) (o2.getTimeSpent() - o1.getTimeSpent());
}
});

double total = 0;
double top20total = 0;
for (int i = 0; i < totalList.size(); i++) {
Node node = totalList.get(i);
double timeSpent = node.getTimeSpent();
total += timeSpent;
if (i < 20) {
top20total += timeSpent;
}
}

VConsole.log("Largest individual contributors using " + top20total
+ " ms out of " + total + " ms");
for (int i = 0; i < 20 && i < totalList.size(); i++) {
Node node = totalList.get(i);
double timeSpent = node.getTimeSpent();
total += timeSpent;
VConsole.log(" * " + node.getName() + ": " + timeSpent + " ms in "
+ node.getCount() + " invokations.");
}

}

/**
* Overridden in {@link EnabledProfiler} to make {@link #isEnabled()} return
* true if GWT.create returns that class.
*
* @return <code>true</code> if the profiling is enabled, else
* <code>false</code>
*/
protected boolean isImplEnabled() {
return false;
}

}

+ 17
- 0
client/src/com/vaadin/client/VDebugConsole.java View File

@@ -513,6 +513,23 @@ public class VDebugConsole extends VOverlay implements Console {
// consoleLog(u.getChildrenAsXML());
}

/**
* Adds a {@link SimpleTree} to the console and prints a string
* representation of the tree structure to the text console.
*
* @param tree
* the simple tree to display in the console window
* @param stringRepresentation
* the string representation of the tree to output to the text
* console
*/
public void showTree(SimpleTree tree, String stringRepresentation) {
if (panel.isAttached()) {
panel.add(tree);
}
consoleLog(stringRepresentation);
}

private static native void consoleDir(ValueMap u)
/*-{
if($wnd.console && $wnd.console.log) {

+ 4
- 0
client/src/com/vaadin/client/WidgetSet.java View File

@@ -47,6 +47,7 @@ public class WidgetSet {
* some hacks. Extra instantiation code is needed if client side
* connector has no "native" counterpart on client side.
*/
Profiler.enter("WidgetSet.createConnector");

Class<? extends ServerConnector> classType = resolveInheritedConnectorType(
conf, tag);
@@ -56,6 +57,7 @@ public class WidgetSet {
UnknownComponentConnector c = GWT
.create(UnknownComponentConnector.class);
c.setServerSideClassName(serverSideName);
Profiler.leave("WidgetSet.createConnector");
return c;
} else {
/*
@@ -68,8 +70,10 @@ public class WidgetSet {
((HasJavaScriptConnectorHelper) connector)
.getJavascriptConnectorHelper().setTag(tag);
}
Profiler.leave("WidgetSet.createConnector");
return connector;
} catch (NoDataException e) {
Profiler.leave("WidgetSet.createConnector");
throw new IllegalStateException(
"There is no information about "
+ classType

+ 11
- 0
client/src/com/vaadin/client/communication/JsonDecoder.java View File

@@ -32,6 +32,7 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.FastStringSet;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.Profiler;
import com.vaadin.client.metadata.NoDataException;
import com.vaadin.client.metadata.Property;
import com.vaadin.client.metadata.Type;
@@ -128,20 +129,24 @@ public class JsonDecoder {

private static Object decodeObject(Type type, JSONValue jsonValue,
Object target, ApplicationConnection connection) {
Profiler.enter("JsonDecoder.decodeObject");
JSONSerializer<Object> serializer = (JSONSerializer<Object>) type
.findSerializer();
if (serializer != null) {
if (target != null && serializer instanceof DiffJSONSerializer<?>) {
DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
diffSerializer.update(target, type, jsonValue, connection);
Profiler.leave("JsonDecoder.decodeObject");
return target;
} else {
Object object = serializer.deserialize(type, jsonValue,
connection);
Profiler.leave("JsonDecoder.decodeObject");
return object;
}
} else {
try {
Profiler.enter("JsonDecoder.decodeObject meta data processing");
JsArrayObject<Property> properties = type
.getPropertiesAsArray();
if (target == null) {
@@ -167,12 +172,18 @@ public class JsonDecoder {
propertyReference = null;
}

Profiler.leave("JsonDecoder.decodeObject meta data processing");
Object decodedValue = decodeValue(propertyType,
encodedPropertyValue, propertyReference, connection);
Profiler.enter("JsonDecoder.decodeObject meta data processing");
property.setValue(target, decodedValue);
}
Profiler.leave("JsonDecoder.decodeObject meta data processing");
Profiler.leave("JsonDecoder.decodeObject");
return target;
} catch (NoDataException e) {
Profiler.leave("JsonDecoder.decodeObject meta data processing");
Profiler.leave("JsonDecoder.decodeObject");
throw new RuntimeException("Can not deserialize "
+ type.getSignature(), e);
}

+ 23
- 0
client/src/com/vaadin/client/ui/AbstractConnector.java View File

@@ -29,6 +29,7 @@ import com.google.gwt.event.shared.HandlerManager;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.FastStringMap;
import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
@@ -110,11 +111,19 @@ public abstract class AbstractConnector implements ServerConnector,
@Override
public final void doInit(String connectorId,
ApplicationConnection connection) {
Profiler.enter("AbstractConnector.doInit");
this.connection = connection;
id = connectorId;

addStateChangeHandler(this);
if (Profiler.isEnabled()) {
Profiler.enter("AbstractConnector.init " + Util.getSimpleName(this));
}
init();
if (Profiler.isEnabled()) {
Profiler.leave("AbstractConnector.init " + Util.getSimpleName(this));
}
Profiler.leave("AbstractConnector.doInit");
}

/**
@@ -200,6 +209,12 @@ public abstract class AbstractConnector implements ServerConnector,

@Override
public void fireEvent(GwtEvent<?> event) {
String profilerKey = null;
if (Profiler.isEnabled()) {
profilerKey = "Fire " + Util.getSimpleName(event) + " for "
+ Util.getSimpleName(this);
Profiler.enter(profilerKey);
}
if (handlerManager != null) {
handlerManager.fireEvent(event);
}
@@ -214,6 +229,10 @@ public abstract class AbstractConnector implements ServerConnector,
}
}
}
if (Profiler.isEnabled()) {
Profiler.leave(profilerKey);
}

}

protected HandlerManager ensureHandlerManager() {
@@ -263,6 +282,7 @@ public abstract class AbstractConnector implements ServerConnector,

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
Profiler.enter("AbstractConnector.onStateChanged");
if (debugLogging) {
VConsole.log("State change event for "
+ Util.getConnectorString(stateChangeEvent.getConnector())
@@ -270,6 +290,7 @@ public abstract class AbstractConnector implements ServerConnector,
}

updateEnabledState(isEnabled());
Profiler.leave("AbstractConnector.onStateChanged");
}

/*
@@ -296,7 +317,9 @@ public abstract class AbstractConnector implements ServerConnector,
@Override
public SharedState getState() {
if (state == null) {
Profiler.enter("AbstractConnector.createState()");
state = createState();
Profiler.leave("AbstractConnector.createState()");
}

return state;

+ 5
- 0
client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java View File

@@ -37,6 +37,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.client.Profiler;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
@@ -659,6 +660,8 @@ public class VDragAndDropManager {
if (serverCallback == null) {
return;
}
Profiler.enter("VDragAndDropManager.handleServerResponse");

UIDL uidl = (UIDL) valueMap.cast();
int visitId = uidl.getIntAttribute("visitId");

@@ -668,6 +671,8 @@ public class VDragAndDropManager {
serverCallback = null;
}
runDeferredCommands();

Profiler.leave("VDragAndDropManager.handleServerResponse");
}

private void runDeferredCommands() {

+ 15
- 0
client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java View File

@@ -23,6 +23,7 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.Util;
import com.vaadin.client.communication.StateChangeEvent;
@@ -281,6 +282,7 @@ public abstract class AbstractOrderedLayoutConnector extends
*/
@Override
public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
Profiler.enter("AOLC.onConnectorHierarchyChange");

List<ComponentConnector> previousChildren = event.getOldChildren();
int currentIndex = 0;
@@ -289,14 +291,22 @@ public abstract class AbstractOrderedLayoutConnector extends
layout.setSpacing(getState().spacing);

for (ComponentConnector child : getChildComponents()) {
Profiler.enter("AOLC.onConnectorHierarchyChange add children");
Slot slot = layout.getSlot(child.getWidget());
if (slot.getParent() != layout) {
Profiler.enter("AOLC.onConnectorHierarchyChange add state change handler");
child.addStateChangeHandler(childStateChangeHandler);
Profiler.leave("AOLC.onConnectorHierarchyChange add state change handler");
}
Profiler.enter("AOLC.onConnectorHierarchyChange addOrMoveSlot");
layout.addOrMoveSlot(slot, currentIndex++);
Profiler.leave("AOLC.onConnectorHierarchyChange addOrMoveSlot");

Profiler.leave("AOLC.onConnectorHierarchyChange add children");
}

for (ComponentConnector child : previousChildren) {
Profiler.enter("AOLC.onConnectorHierarchyChange remove children");
if (child.getParent() != this) {
Slot slot = layout.getSlot(child.getWidget());
slot.setWidgetResizeListener(null);
@@ -309,7 +319,9 @@ public abstract class AbstractOrderedLayoutConnector extends
child.removeStateChangeHandler(childStateChangeHandler);
layout.removeWidget(child.getWidget());
}
Profiler.leave("AOL.onConnectorHierarchyChange remove children");
}
Profiler.leave("AOLC.onConnectorHierarchyChange");

updateInternalState();
}
@@ -342,6 +354,7 @@ public abstract class AbstractOrderedLayoutConnector extends
if (processedResponseId == lastResponseId) {
return;
}
Profiler.enter("AOLC.updateInternalState");
// Remember that everything is updated for this response
processedResponseId = lastResponseId;

@@ -422,6 +435,8 @@ public abstract class AbstractOrderedLayoutConnector extends
} else {
getWidget().clearExpand();
}

Profiler.leave("AOLC.updateInternalState");
}

/**

Loading…
Cancel
Save