diff options
author | Artur Signell <artur@vaadin.com> | 2013-03-12 12:58:05 +0200 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2013-03-12 12:58:07 +0200 |
commit | 0f3ac51ed4c34c9233463c1274c7107580faa1f9 (patch) | |
tree | 11a1de6982ca79c96c787cf395fe4f1fc51deeb9 /client/src | |
parent | 1853dea1b9277f2d5a8911bd2d20277158e221b7 (diff) | |
parent | 6922bc5b49c5551b289a5025ccd5901e2ac3aafc (diff) | |
download | vaadin-framework-0f3ac51ed4c34c9233463c1274c7107580faa1f9.tar.gz vaadin-framework-0f3ac51ed4c34c9233463c1274c7107580faa1f9.zip |
Merge commit '6922bc5b49c5551b289a5025ccd5901e2ac3aafc'
Change-Id: Id118fd9bae1fdf7e7ce2efd660ade5ba7bbc5109
Diffstat (limited to 'client/src')
5 files changed, 267 insertions, 48 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java index 9ba660626e..2291f21361 100644 --- a/client/src/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/com/vaadin/client/ApplicationConfiguration.java @@ -535,7 +535,7 @@ public class ApplicationConfiguration implements EntryPoint { @Override public void onModuleLoad() { - Profiler.reset(); + Profiler.initialize(); Profiler.enter("ApplicationConfiguration.onModuleLoad"); BrowserInfo browserInfo = BrowserInfo.get(); diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 1a637e3161..62827feffb 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -16,7 +16,7 @@ package com.vaadin.client; -import java.util.ArrayList; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -500,6 +500,7 @@ public class ApplicationConnection { ap.@com.vaadin.client.ApplicationConnection::totalProcessingTime ]; pd = pd.concat(ap.@com.vaadin.client.ApplicationConnection::serverTimingInfo); + pd[pd.length] = ap.@com.vaadin.client.ApplicationConnection::bootstrapTime; return pd; }); @@ -513,6 +514,16 @@ public class ApplicationConnection { $wnd.vaadin.clients[TTAppId] = client; }-*/; + private static native final int calculateBootstrapTime() + /*-{ + if ($wnd.performance && $wnd.performance.timing) { + return (new Date).getTime() - $wnd.performance.timing.responseStart; + } else { + // performance.timing not supported + return -1; + } + }-*/; + /** * Helper for tt initialization */ @@ -948,6 +959,15 @@ public class ApplicationConnection { protected int totalProcessingTime; /** + * Holds the time it took to load the page and render the first view. 0 + * means that this value has not yet been calculated because the first view + * has not yet been rendered (or that your browser is very fast). -1 means + * that the browser does not support the performance.timing feature used to + * get this measurement. + */ + private int bootstrapTime; + + /** * Holds the timing information from the server-side. How much time was * spent servicing the last request and how much time has been spent * servicing the session so far. These values are always one request behind, @@ -1512,6 +1532,12 @@ public class ApplicationConnection { lastProcessingTime = (int) ((new Date().getTime()) - start .getTime()); totalProcessingTime += lastProcessingTime; + if (bootstrapTime == 0) { + bootstrapTime = calculateBootstrapTime(); + if (Profiler.isEnabled() && bootstrapTime != -1) { + Profiler.logBootstrapTimings(); + } + } VConsole.log(" Processing time was " + String.valueOf(lastProcessingTime) + "ms for " @@ -1775,7 +1801,7 @@ public class ApplicationConnection { .getConnectorClassByEncodedTag(connectorType); // Connector does not exist so we must create it - if (connectorClass != UIConnector.class) { + if (connectorClass != uIConnector.getClass()) { // create, initialize and register the paintable Profiler.enter("ApplicationConnection.getConnector"); connector = getConnector(connectorId, connectorType); diff --git a/client/src/com/vaadin/client/Profiler.java b/client/src/com/vaadin/client/Profiler.java index be26da859f..6e8f2f5f9a 100644 --- a/client/src/com/vaadin/client/Profiler.java +++ b/client/src/com/vaadin/client/Profiler.java @@ -54,25 +54,45 @@ public class Profiler { } } - private static JsArray<ProfilerEvent> events; + private static final String evtGroup = "VaadinProfiler"; - private static final class ProfilerEvent extends JavaScriptObject { - protected ProfilerEvent() { + private static final class GwtStatsEvent extends JavaScriptObject { + protected GwtStatsEvent() { // JSO constructor } - public native String getName() + private native String getEvtGroup() /*-{ - return this.name; + return this.evtGroup; }-*/; - private native double getRawTime() + private native double getMillis() /*-{ - return this.time; + return this.millis; }-*/; - private boolean isStart() { - return getRawTime() <= 0; + private native String getSubSystem() + /*-{ + return this.subSystem; + }-*/; + + private native String getType() + /*-{ + return this.type; + }-*/; + + private native String getModuleName() + /*-{ + return this.moduleName; + }-*/; + + public final String getEventName() { + String group = getEvtGroup(); + if (evtGroup.equals(group)) { + return getSubSystem(); + } else { + return group + "." + getSubSystem(); + } } } @@ -91,21 +111,17 @@ public class Profiler { return name; } - public Node addEvent(ProfilerEvent event) { - Node child = children.get(event.getName()); + private Node accessChild(String name, double time) { + Node child = children.get(name); if (child == null) { - child = new Node(event.getName()); - children.put(event.getName(), child); + child = new Node(name); + children.put(name, child); } - child.time += event.getRawTime(); + child.time -= time; child.count++; return child; } - public void registerEnd(ProfilerEvent event) { - time += event.getRawTime(); - } - public double getTimeSpent() { return time; } @@ -148,6 +164,11 @@ public class Profiler { } } + @Override + public String toString() { + return getStringRepresentation(""); + } + private String getStringRepresentation(String prefix) { if (getName() == null) { return ""; @@ -229,7 +250,7 @@ public class Profiler { */ public static void enter(String name) { if (isEnabled()) { - pushEvent(events, name, -Duration.currentTimeMillis()); + logGwtEvent(name, "begin"); } } @@ -243,14 +264,20 @@ public class Profiler { */ public static void leave(String name) { if (isEnabled()) { - pushEvent(events, name, Duration.currentTimeMillis()); + logGwtEvent(name, "end"); } } - private static native final void pushEvent(JsArray<ProfilerEvent> target, - String name, double time) + private static native final void logGwtEvent(String name, String type) /*-{ - target[target.length] = {name: name, time: time}; + $wnd.__gwtStatsEvent({ + evtGroup: @com.vaadin.client.Profiler::evtGroup, + moduleName: @com.google.gwt.core.client.GWT::getModuleName()(), + millis: (new Date).getTime(), + sessionId: undefined, + subSystem: name, + type: type + }); }-*/; /** @@ -259,7 +286,35 @@ public class Profiler { */ public static void reset() { if (isEnabled()) { - events = JavaScriptObject.createArray().cast(); + /* + * Old implementations might call reset for initialization, so + * ensure it is initialized here as well. Initialization has no side + * effects if already done. + */ + initialize(); + + clearEventsList(); + } + } + + /** + * Initializes the profiler. This should be done before calling any other + * function in this class. Failing to do so might cause undesired behavior. + * This method has no side effects if the initialization has already been + * done. + * <p> + * Please note that this method should be called even if the profiler is not + * enabled because it will then remove a logger function that might have + * been included in the HTML page and that would leak memory unless removed. + * </p> + * + * @since 7.0.2 + */ + public static void initialize() { + if (isEnabled()) { + ensureLogger(); + } else { + ensureNoLogger(); } } @@ -275,25 +330,52 @@ public class Profiler { 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."); + JsArray<GwtStatsEvent> gwtStatsEvents = getGwtStatsEvents(); + if (gwtStatsEvents.length() == 0) { + VConsole.log("No profiling events recorded, this might happen if another __gwtStatsEvent handler is installed."); + return; + } + + for (int i = 0; i < gwtStatsEvents.length(); i++) { + GwtStatsEvent gwtStatsEvent = gwtStatsEvents.get(i); + String eventName = gwtStatsEvent.getEventName(); + String type = gwtStatsEvent.getType(); + boolean isBeginEvent = "begin".equals(type); + + Node stackTop = stack.getLast(); + boolean inEvent = eventName.equals(stackTop.getName()) + && !isBeginEvent; + + if (!inEvent && stack.size() >= 2 + && eventName.equals(stack.get(stack.size() - 2).name) + && !isBeginEvent) { + // back out of sub event + stackTop.time += gwtStatsEvent.getMillis(); + stack.removeLast(); + stackTop = stack.getLast(); + + inEvent = true; + } + + if (type.equals("end")) { + if (!inEvent) { + VConsole.error("Got end event for " + eventName + + " but is currently in " + stackTop.getName()); return; } - if (!stackTop.getName().equals(event.getName())) { - VConsole.error("Invalid profiling event order, leaving " - + event.getName() + " but " + stackTop.getName() - + " was expected"); - return; + Node previousStackTop = stack.removeLast(); + previousStackTop.time += gwtStatsEvent.getMillis(); + } else { + if (!inEvent) { + stackTop = stackTop.accessChild(eventName, + gwtStatsEvent.getMillis()); + stack.add(stackTop); + } + if (!isBeginEvent) { + // Create sub event + stack.add(stackTop.accessChild(eventName + "." + type, + gwtStatsEvent.getMillis())); } - stackTop.registerEnd(event); } } @@ -362,4 +444,102 @@ public class Profiler { return false; } + /** + * Outputs the time passed since various events recored in + * performance.timing if supported by the browser. + */ + public static void logBootstrapTimings() { + if (isEnabled()) { + double now = Duration.currentTimeMillis(); + + StringBuilder stringBuilder = new StringBuilder( + "Time since window.performance.timing events"); + SimpleTree tree = new SimpleTree(stringBuilder.toString()); + + String[] keys = new String[] { "navigationStart", + "unloadEventStart", "unloadEventEnd", "redirectStart", + "redirectEnd", "fetchStart", "domainLookupStart", + "domainLookupEnd", "connectStart", "connectEnd", + "requestStart", "responseStart", "responseEnd", + "domLoading", "domInteractive", + "domContentLoadedEventStart", "domContentLoadedEventEnd", + "domComplete", "loadEventStart", "loadEventEnd" }; + + for (String key : keys) { + double value = getPerformanceTiming(key); + if (value == 0) { + // Ignore missing value + continue; + } + String text = key + ": " + (now - value); + tree.add(new Label(text)); + stringBuilder.append("\n * "); + stringBuilder.append(text); + } + + if (tree.getWidgetCount() == 0) { + VConsole.log("Bootstrap timings not supported, please ensure your browser supports performance.timing"); + return; + } + + Console implementation = VConsole.getImplementation(); + if (implementation instanceof VDebugConsole) { + VDebugConsole console = (VDebugConsole) implementation; + console.showTree(tree, stringBuilder.toString()); + } else { + VConsole.log(stringBuilder.toString()); + } + } + } + + private static final native double getPerformanceTiming(String name) + /*-{ + if ($wnd.performance && $wnd.performance.timing && $wnd.performance.timing[name]) { + return $wnd.performance.timing[name]; + } else { + return 0; + } + }-*/; + + private static native JsArray<GwtStatsEvent> getGwtStatsEvents() + /*-{ + return $wnd.vaadin.gwtStatsEvents || []; + }-*/; + + /** + * Add logger if it's not already there, also initializing the event array + * if needed. + */ + private static native void ensureLogger() + /*-{ + if (typeof $wnd.__gwtStatsEvent != 'function') { + if (typeof $wnd.vaadin.gwtStatsEvents != 'object') { + $wnd.vaadin.gwtStatsEvents = []; + } + $wnd.__gwtStatsEvent = function(event) { + $wnd.vaadin.gwtStatsEvents.push(event); + return true; + } + } + }-*/; + + /** + * Remove logger function and event array if it seems like the function has + * been added by us. + */ + private static native void ensureNoLogger() + /*-{ + if (typeof $wnd.vaadin.gwtStatsEvents == 'object') { + delete $wnd.vaadin.gwtStatsEvents; + if (typeof $wnd.__gwtStatsEvent == 'function') { + $wnd.__gwtStatsEvent = function(){}; + } + } + }-*/; + + private static native JsArray<GwtStatsEvent> clearEventsList() + /*-{ + $wnd.vaadin.gwtStatsEvents = []; + }-*/; + } diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index c76dd38d8f..6f9fd6da88 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1098,6 +1098,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } + private ScheduledCommand lazyScroller = new ScheduledCommand() { + @Override + public void execute() { + int offsetTop = measureRowHeightOffset(firstvisible); + scrollBodyPanel.setScrollPosition(offsetTop); + } + }; + /** For internal use only. May be removed or replaced in the future. */ public void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) { firstvisible = uidl.hasVariable("firstvisible") ? uidl @@ -1105,8 +1113,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (firstvisible != lastRequestedFirstvisible && scrollBody != null) { // received 'surprising' firstvisible from server: scroll there firstRowInViewPort = firstvisible; - scrollBodyPanel - .setScrollPosition(measureRowHeightOffset(firstvisible)); + + /* + * Schedule the scrolling to be executed last so no updates to the rows + * affect scrolling measurements. + */ + Scheduler.get().scheduleFinally(lazyScroller); } } diff --git a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java index bce4242557..c6e4883fa9 100644 --- a/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java +++ b/client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java @@ -137,7 +137,8 @@ public class EmbeddedConnector extends AbstractComponentConnector implements getWidget().getSrc(uidl, client)); clearBrowserElement = false; } else { - VConsole.log("Unknown Embedded type '" + getWidget().type + "'"); + VConsole.error("Unknown Embedded type '" + getWidget().type + + "'"); } } else if (uidl.hasAttribute("mimetype")) { // remove old style name related to type @@ -203,10 +204,10 @@ public class EmbeddedConnector extends AbstractComponentConnector implements .getStringAttribute(EmbeddedConstants.ALTERNATE_TEXT)); } } else { - VConsole.log("Unknown Embedded mimetype '" + mime + "'"); + VConsole.error("Unknown Embedded mimetype '" + mime + "'"); } } else { - VConsole.log("Unknown Embedded; no type or mimetype attribute"); + VConsole.error("Unknown Embedded; no type or mimetype attribute"); } if (clearBrowserElement) { |