summaryrefslogtreecommitdiffstats
path: root/client/src
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2013-03-12 12:58:05 +0200
committerArtur Signell <artur@vaadin.com>2013-03-12 12:58:07 +0200
commit0f3ac51ed4c34c9233463c1274c7107580faa1f9 (patch)
tree11a1de6982ca79c96c787cf395fe4f1fc51deeb9 /client/src
parent1853dea1b9277f2d5a8911bd2d20277158e221b7 (diff)
parent6922bc5b49c5551b289a5025ccd5901e2ac3aafc (diff)
downloadvaadin-framework-0f3ac51ed4c34c9233463c1274c7107580faa1f9.tar.gz
vaadin-framework-0f3ac51ed4c34c9233463c1274c7107580faa1f9.zip
Merge commit '6922bc5b49c5551b289a5025ccd5901e2ac3aafc'
Change-Id: Id118fd9bae1fdf7e7ce2efd660ade5ba7bbc5109
Diffstat (limited to 'client/src')
-rw-r--r--client/src/com/vaadin/client/ApplicationConfiguration.java2
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java30
-rw-r--r--client/src/com/vaadin/client/Profiler.java260
-rw-r--r--client/src/com/vaadin/client/ui/VScrollTable.java16
-rw-r--r--client/src/com/vaadin/client/ui/embedded/EmbeddedConnector.java7
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) {