Repository: incubator-wave
Updated Branches:
  refs/heads/master 47cd2e99f -> d88962765


http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/box/webclient/stat/gwtevent/GwtStatisticsHandler.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/box/webclient/stat/gwtevent/GwtStatisticsHandler.java 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/GwtStatisticsHandler.java
new file mode 100644
index 0000000..f3096c1
--- /dev/null
+++ b/src/org/waveprotocol/box/webclient/stat/gwtevent/GwtStatisticsHandler.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.box.webclient.stat.gwtevent;
+
+import org.waveprotocol.box.stat.Timer;
+import org.waveprotocol.box.stat.Timing;
+
+import java.util.Iterator;
+
+/**
+ * Transformer of GWT statistic.
+ *
+ * @author [email protected] (A. Kaplanov)
+ */
+public class GwtStatisticsHandler implements StatisticsEventListener {
+  static private String GWT_PREFIX = "GWT.";
+
+  static private String PARAM_TYPE = "type";
+  static private String TYPE_BEGIN = "begin";
+  static private String TYPE_END = "end";
+
+  private Timer requestTimer;
+  private String currentGroup;
+  private long previousCallTimestamp;
+
+  @Override
+  public void onStatisticsEvent(StatisticsEvent event) {
+    String group = event.getEventGroupKey();
+    String type = (String)event.getExtraParameter(PARAM_TYPE);
+    if (TYPE_BEGIN.equals(type)) {
+      enterGroup(group, (long)event.getMillis());
+    } else {
+      if (TYPE_END.equals(type)) {
+        leaveGroup((long)event.getMillis());
+      } else {
+        if (!group.equals(currentGroup)) {
+          leaveGroup((long)event.getMillis());
+          enterGroup(group, (long)event.getMillis());
+        }
+        Timing.record(GWT_PREFIX + type, 
(int)((long)event.getMillis()-previousCallTimestamp));
+      }
+    }
+    previousCallTimestamp = (long)event.getMillis();
+  }
+
+  private void enterGroup(String group, long time) {
+    if (Timing.isEnabled()) {
+      Timing.enterScope();
+      requestTimer = Timing.startRequest(GWT_PREFIX + group);
+      requestTimer.start(time);
+      currentGroup = group;
+    }
+  }
+
+  private void leaveGroup(long time) {
+    if (Timing.isEnabled() && currentGroup != null) {
+      Timing.stop(requestTimer, time);
+      requestTimer = null;
+      currentGroup = null;
+      Timing.exitScope();
+    }
+  }
+
+  private static String getExtraParameters(StatisticsEvent event) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("{");
+    Iterator<String> names = event.getExtraParameterNames();
+    if (names.hasNext()) {
+      String n = names.next();
+      sb.append(n).append(" = ").append(event.getExtraParameter(n));
+      while (names.hasNext()) {
+        sb.append(", ").append(n = names.next()).append(" = 
").append(event.getExtraParameter(n));
+      }
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEvent.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEvent.java 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEvent.java
new file mode 100644
index 0000000..d833e67
--- /dev/null
+++ b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEvent.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.box.webclient.stat.gwtevent;
+
+import java.util.Iterator;
+
+/**
+ * Java "Overlay" object of the JavaScript event objects fired by the stats
+ * system.
+ */
+public interface StatisticsEvent {
+
+  /**
+   * Answers with the name of the module that caused this event.
+   */
+  public String getModuleName();
+
+  /**
+   * Answers with the name of the sub system (rpc, boot strap, etc..) that
+   * caused this event.
+   */
+  public String getSubSystem();
+
+  /**
+   * Answers with a key unique to the group of events that this event belongs
+   * to. Along with the module and sub system this forms a unique key for
+   * timing related events. Note that each group requires at least two events:
+   * one to signal the start of the measured period and one to signal the end.
+   */
+  public String getEventGroupKey();
+
+  /**
+   * Answers with the time stamp (millis since the epoch) at which this event
+   * occurred. Using double, since long is not natively supported in
+   * JavaScript (see {@link com.google.gwt.core.client.Duration}).
+   */
+  public double getMillis();
+
+  /**
+   * Answers with a read-only iterator over the names of any extra parameters
+   * associated with this event.
+   */
+  public Iterator<String> getExtraParameterNames();
+
+  /**
+   * Answers with the given named extra parameter. Since most events are fired
+   * from within JavaScript or another module, the returned value is either a
+   * String, Double, Boolean or a JavaScriptObject.
+   */
+  public Object getExtraParameter(String name);
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventDispatcher.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventDispatcher.java
 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventDispatcher.java
new file mode 100644
index 0000000..8809059
--- /dev/null
+++ 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventDispatcher.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.box.webclient.stat.gwtevent;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Helps with creating and dispatching the events for the debug panel.
+ */
+public interface StatisticsEventDispatcher {
+
+  /**
+   * Returns {@code true} if there is somebody listening to events on the other
+   * side.
+   */
+  public boolean enabled();
+
+  /**
+   * Creates a new event object to be dispatched.
+
+   * @see StatisticsEvent
+   */
+  public StatisticsEvent newEvent(String system, String group, double millis, 
String type);
+
+  /**
+   * Sets the given extra parameter's value as a JavaScriptObject.
+   *
+   * @param event the event to set the parameter on. This has to be an instance
+   *        returned by @{link {@link #newEvent(String, String, double, 
String)}.
+   */
+  public void setExtraParameter(StatisticsEvent event, String name, 
JavaScriptObject value);
+
+  /**
+   * Sets the given extra parameter's value as a String.
+   *
+   * @param event the event to set the parameter on. This has to be an instance
+   *        returned by @{link {@link #newEvent(String, String, double, 
String)}.
+   */
+  public void setExtraParameter(StatisticsEvent event, String name, String 
value);
+
+  /**
+   * Dispatches the given event.
+   *
+   * @param event the event to dispatch. This has to be an instance
+   *        returned by @{link {@link #newEvent(String, String, double, 
String)}.
+   */
+  public void dispatch(StatisticsEvent event);
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventListener.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventListener.java 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventListener.java
new file mode 100644
index 0000000..42b9497
--- /dev/null
+++ 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventListener.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.box.webclient.stat.gwtevent;
+
+
+/**
+ * Receiver of {@link StatisticsEvent} events.
+ */
+public interface StatisticsEventListener {
+  public void onStatisticsEvent(StatisticsEvent event);
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventSystem.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventSystem.java 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventSystem.java
new file mode 100644
index 0000000..e4bd203
--- /dev/null
+++ 
b/src/org/waveprotocol/box/webclient/stat/gwtevent/StatisticsEventSystem.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.box.webclient.stat.gwtevent;
+
+import java.util.Iterator;
+
+/**
+ * The statistics event system is responsible for managing the statistics
+ * {@link StatisticsEventListener event listeners} and dispatching the
+ * {@link StatisticsEvent events}.
+ */
+public interface StatisticsEventSystem {
+
+  /**
+   * Register a listener to receive future {@link StatisticsEvent events}.
+   *
+   * @param replay if true, past (recorded) events will be replayed on the
+   *               given listener.
+   */
+  public void addListener(StatisticsEventListener listener, boolean replay);
+
+  /**
+   * Removes a listener so it will no longer receive any
+   * {@link StatisticsEvent events}.
+   */
+  public void removeListener(StatisticsEventListener listener);
+
+  /**
+   * Provides read-only access to all the recorded events.
+   */
+  public Iterator<StatisticsEvent> pastEvents();
+
+  /**
+   * Clears the event history.
+   */
+  public void clearEventHistory();
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/Stages.java
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/Stages.java 
b/src/org/waveprotocol/wave/client/Stages.java
index 52ade26..f0cab51 100644
--- a/src/org/waveprotocol/wave/client/Stages.java
+++ b/src/org/waveprotocol/wave/client/Stages.java
@@ -20,12 +20,15 @@
 
 package org.waveprotocol.wave.client;
 
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.user.client.Command;
+import org.waveprotocol.box.stat.Timer;
+import org.waveprotocol.box.stat.Timing;
 
 import org.waveprotocol.wave.client.common.util.AsyncHolder;
 import org.waveprotocol.wave.client.common.util.AsyncHolder.Accessor;
+import org.waveprotocol.wave.client.scheduler.Scheduler;
+import org.waveprotocol.wave.client.scheduler.SchedulerInstance;
 
 /**
  * Loads Undercurrent's stages in order.
@@ -71,18 +74,22 @@ public abstract class Stages {
   }
 
   private void loadStageZero() {
+    final Timer timer = Timing.start("Stage Zero");
     createStageZeroLoader().call(new Accessor<StageZero>() {
       @Override
       public void use(StageZero x) {
+        Timing.stop(timer);
         loadStageOne(x);
       }
     });
   }
 
   private void loadStageOne(final StageZero zero) {
+    final Timer timer = Timing.start("Stage One");
     createStageOneLoader(zero).call(new Accessor<StageOne>() {
       @Override
       public void use(StageOne x) {
+        Timing.stop(timer);
         loadStageTwo(x);
       }
     });
@@ -90,12 +97,15 @@ public abstract class Stages {
 
 
   private void loadStageTwo(final StageOne one) {
-    GWT.runAsync(new SimpleAsyncCallback() {
+    final Timer timer = Timing.start("Stage Two");
+    SchedulerInstance.getHighPriorityTimer().schedule(new Scheduler.Task() {
+
       @Override
-      public void onSuccess() {
+      public void execute() {
         createStageTwoLoader(one).call(new Accessor<StageTwo>() {
           @Override
           public void use(StageTwo x) {
+            Timing.stop(timer);
             loadStageThree(x);
           }
         });
@@ -104,12 +114,15 @@ public abstract class Stages {
   }
 
   private void loadStageThree(final StageTwo two) {
-    GWT.runAsync(new SimpleAsyncCallback() {
+    final Timer timer = Timing.start("Stage Tree");
+    SchedulerInstance.getHighPriorityTimer().schedule(new Scheduler.Task() {
+
       @Override
-      public void onSuccess() {
+      public void execute() {
         createStageThreeLoader(two).call(new Accessor<StageThree>() {
           @Override
           public void use(StageThree x) {
+            Timing.stop(timer);
             finish();
           }
         });

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/scheduler/BrowserBackedScheduler.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/wave/client/scheduler/BrowserBackedScheduler.java 
b/src/org/waveprotocol/wave/client/scheduler/BrowserBackedScheduler.java
index 6cc57e6..2b54546 100644
--- a/src/org/waveprotocol/wave/client/scheduler/BrowserBackedScheduler.java
+++ b/src/org/waveprotocol/wave/client/scheduler/BrowserBackedScheduler.java
@@ -22,6 +22,8 @@
 package org.waveprotocol.wave.client.scheduler;
 
 import com.google.gwt.user.client.ui.Widget;
+import org.waveprotocol.box.stat.Timer;
+import org.waveprotocol.box.stat.Timing;
 
 import org.waveprotocol.wave.model.util.CollectionUtils;
 import org.waveprotocol.wave.model.util.CopyOnWriteSet;
@@ -272,30 +274,43 @@ public class BrowserBackedScheduler implements Scheduler {
 
     double start = timer.getTime();
 
-    if (job instanceof IncrementalTask) {
-      boolean isFinished = !jobs.getRemovedJobAsProcess().execute();
-
-      if (isFinished) {
-        // Remove all trace
-        cancel(job);
-      } else {
-        TaskInfo task = taskInfos.get(job);
-        // If the job has more work to do, we add it back into the job queue, 
unless it has has
-        // already been cancelled during execution (which would imply !hasJob)
-        if (task != null && hasJob(job)) {
-          // if it is a repeating job, add it to a delay before we contiune
-          if (task.calculateNextExecuteTime(start)) {
-            delayedJobs.addDelayedJob(task);
-          } else if (!delayedJobs.has(task.id)) {
-            jobs.add(priority, job);
+    TaskInfo taskInfo = taskInfos.get(job);
+    Timer profilingTimer = null;
+    if (taskInfo != null && Timing.isEnabled()) {
+      Timing.enterScope(taskInfo.scopeValues);
+      profilingTimer = Timing.start("schedule " + 
job.getClass().getSimpleName());
+    }
+    try {
+      if (job instanceof IncrementalTask) {
+        boolean isFinished = !jobs.getRemovedJobAsProcess().execute();
+
+        if (isFinished) {
+          // Remove all trace
+          cancel(job);
+        } else {
+          TaskInfo task = taskInfos.get(job);
+          // If the job has more work to do, we add it back into the job 
queue, unless it has has
+          // already been cancelled during execution (which would imply 
!hasJob)
+          if (task != null && hasJob(job)) {
+            // if it is a repeating job, add it to a delay before we contiune
+            if (task.calculateNextExecuteTime(start)) {
+              delayedJobs.addDelayedJob(task);
+            } else if (!delayedJobs.has(task.id)) {
+              jobs.add(priority, job);
+            }
           }
         }
+      } else {
+        Task task = jobs.getRemovedJobAsTask();
+        // Remove all trace.
+        cancel(job);
+        task.execute();
+      }
+    } finally {
+      if (profilingTimer != null) {
+        Timing.stop(profilingTimer);
+        Timing.exitScope();
       }
-    } else {
-      Task task = jobs.getRemovedJobAsTask();
-      // Remove all trace.
-      cancel(job);
-      task.execute();
     }
 
     int timeSpent = (int) ( timer.getTime() - start);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/scheduler/TaskInfo.java
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/scheduler/TaskInfo.java 
b/src/org/waveprotocol/wave/client/scheduler/TaskInfo.java
index 23c3f97..420807e 100644
--- a/src/org/waveprotocol/wave/client/scheduler/TaskInfo.java
+++ b/src/org/waveprotocol/wave/client/scheduler/TaskInfo.java
@@ -20,15 +20,20 @@
 
 package org.waveprotocol.wave.client.scheduler;
 
+import org.waveprotocol.box.stat.RequestScope;
+import org.waveprotocol.box.stat.Timing;
 import org.waveprotocol.wave.client.scheduler.Scheduler.Priority;
 import org.waveprotocol.wave.client.scheduler.Scheduler.Schedulable;
 
+import java.util.Map;
+
 /**
  * Some information about a scheduled task.
  * Gives each a unique id, and also stores the priority.
  *
  * @author [email protected] (Daniel Danilatos)
  */
+@SuppressWarnings("rawtypes")
 final class TaskInfo {
   private static int nextId;
   final String id = Integer.toString(++nextId);
@@ -36,6 +41,7 @@ final class TaskInfo {
   final double startTime;
   final double interval;
   final Schedulable job;
+  final Map<Class, RequestScope.Value> scopeValues;
   private double nextExecuteTime;
 
   public TaskInfo(Priority p, Schedulable job) {
@@ -48,6 +54,7 @@ final class TaskInfo {
     this.interval = interval;
     this.nextExecuteTime = startTime;
     this.job = job;
+    this.scopeValues = Timing.getScope().cloneValues();
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/wavepanel/event/EventDispatcherPanel.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/wave/client/wavepanel/event/EventDispatcherPanel.java 
b/src/org/waveprotocol/wave/client/wavepanel/event/EventDispatcherPanel.java
index cb0822a..24313e8 100644
--- a/src/org/waveprotocol/wave/client/wavepanel/event/EventDispatcherPanel.java
+++ b/src/org/waveprotocol/wave/client/wavepanel/event/EventDispatcherPanel.java
@@ -37,6 +37,8 @@ import com.google.gwt.event.dom.client.MouseDownHandler;
 import com.google.gwt.user.client.ui.ComplexPanel;
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.Widget;
+import org.waveprotocol.box.stat.Timer;
+import org.waveprotocol.box.stat.Timing;
 
 import org.waveprotocol.wave.client.common.util.LogicalPanel;
 import org.waveprotocol.wave.model.util.CollectionUtils;
@@ -302,7 +304,19 @@ public final class EventDispatcherPanel extends 
ComplexPanel
 
     @Override
     boolean dispatch(ChangeEvent event, Element context, WaveChangeHandler 
handler) {
-      return handler.onChange(event, context);
+      Timer timer = null;
+      if (Timing.isEnabled()) {
+        Timing.enterScope();
+        timer = Timing.start("Mouse event dispatch");
+      }
+      try {
+        return handler.onChange(event, context);
+      } finally {
+        if (timer != null) {
+          Timing.stop(timer);
+          Timing.exitScope();
+        }
+      }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/wavepanel/event/FocusManager.java
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/wavepanel/event/FocusManager.java 
b/src/org/waveprotocol/wave/client/wavepanel/event/FocusManager.java
index 461a3ce..6704ae1 100644
--- a/src/org/waveprotocol/wave/client/wavepanel/event/FocusManager.java
+++ b/src/org/waveprotocol/wave/client/wavepanel/event/FocusManager.java
@@ -24,6 +24,7 @@ import com.google.common.base.Preconditions;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.event.dom.client.KeyDownEvent;
 import com.google.gwt.event.dom.client.KeyDownHandler;
 import com.google.gwt.event.dom.client.KeyEvent;
@@ -34,6 +35,10 @@ import com.google.gwt.event.dom.client.KeyUpHandler;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.ui.ComplexPanel;
 import com.google.gwt.user.client.ui.RootPanel;
+import org.waveprotocol.box.stat.ExecutionTree;
+import org.waveprotocol.box.stat.Timer;
+import org.waveprotocol.box.stat.Timing;
+import org.waveprotocol.box.webclient.stat.dialog.StatDialog;
 
 import org.waveprotocol.wave.client.common.util.EventWrapper;
 import org.waveprotocol.wave.client.common.util.KeyCombo;
@@ -82,7 +87,7 @@ public final class FocusManager implements Focusable, 
KeySignalHandler {
   /** Unique top-level focus manager. */
   private final static FocusManager ROOT = new FocusManager(null, true);
 
-  static {
+  public static void init() {
     DocumentPanel.install(ROOT);
   }
 
@@ -228,6 +233,8 @@ public final class FocusManager implements Focusable, 
KeySignalHandler {
 
     private final KeySignalHandler globalHandler;
 
+    private int statDialogCondition = 0;
+
     private DocumentPanel(KeySignalHandler handler) {
       this.globalHandler = handler;
     }
@@ -256,7 +263,11 @@ public final class FocusManager implements Focusable, 
KeySignalHandler {
 
     @Override
     public void onKeyDown(KeyDownEvent event) {
-      dispatch(event);
+      if (checkStatDialogCondition(event)) {
+        StatDialog.show();
+      } else {
+        dispatch(event);
+      }
     }
 
     @Override
@@ -270,22 +281,54 @@ public final class FocusManager implements Focusable, 
KeySignalHandler {
     }
 
     private void dispatch(KeyEvent<?> event) {
-      // Only respond to key events on the body element. Otherwise, the key
-      // event was probably targeted to some editable input element, and that
-      // should own the events.
-      NativeEvent realEvent = event.getNativeEvent();
-      Element target = realEvent.getEventTarget().cast();
-      if (!"body".equals(target.getTagName().toLowerCase())) {
-        return;
+      Timer timer = null;
+      if (Timing.isEnabled()) {
+        Timing.enterScope();
+        Timing.getScope().set(ExecutionTree.class, new ExecutionTree());
+        timer = Timing.start("Key event dispatch");
       }
-      // Test that the event is meaningful (and stop bubbling if it is not).
-      SignalEvent signal = SignalEventImpl.create(realEvent.<Event>cast(), 
true);
-      if (signal != null) {
-        KeyCombo key = EventWrapper.getKeyCombo(signal);
-        if (globalHandler.onKeySignal(key)) {
-          event.preventDefault();
+      try {
+        // Only respond to key events on the body element. Otherwise, the key
+        // event was probably targeted to some editable input element, and that
+        // should own the events.
+        NativeEvent realEvent = event.getNativeEvent();
+        Element target = realEvent.getEventTarget().cast();
+        if (!"body".equals(target.getTagName().toLowerCase())) {
+          return;
+        }
+        // Test that the event is meaningful (and stop bubbling if it is not).
+        SignalEvent signal = SignalEventImpl.create(realEvent.<Event>cast(), 
true);
+        if (signal != null) {
+          KeyCombo key = EventWrapper.getKeyCombo(signal);
+          if (globalHandler.onKeySignal(key)) {
+            event.preventDefault();
+          }
         }
+      } finally {
+        Timing.stop(timer);
+        Timing.exitScope();
+      }
+    }
+
+    private boolean checkStatDialogCondition(KeyDownEvent event) {
+      int code = event.getNativeEvent().getKeyCode();
+      switch (statDialogCondition) {
+        case 0:
+          if (code == KeyCodes.KEY_CTRL) {
+            statDialogCondition = 1;
+          }
+          break;
+        case 1:
+          statDialogCondition = (code == KeyCodes.KEY_ALT)?2:0;
+          break;
+        case 2:
+          statDialogCondition = 0;
+          if (code == KeyCodes.KEY_CTRL) {
+            return true;
+          }
+          break;
       }
+      return false;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/WavePanelResourceLoader.java
----------------------------------------------------------------------
diff --git 
a/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/WavePanelResourceLoader.java
 
b/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/WavePanelResourceLoader.java
index 2f16cb2..46fa5ac 100644
--- 
a/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/WavePanelResourceLoader.java
+++ 
b/src/org/waveprotocol/wave/client/wavepanel/view/dom/full/WavePanelResourceLoader.java
@@ -26,6 +26,7 @@ import org.waveprotocol.wave.client.editor.EditorImpl;
 import org.waveprotocol.wave.client.wavepanel.view.dom.CssProvider;
 import org.waveprotocol.wave.client.wavepanel.view.dom.full.i18n.BlipMessages;
 import 
org.waveprotocol.wave.client.wavepanel.view.dom.full.i18n.ReplyBoxMessages;
+import org.waveprotocol.wave.client.widget.dialog.Dialog;
 
 /**
  * This class is responsible for loading all the Css resources needed by the
@@ -48,6 +49,8 @@ public final class WavePanelResourceLoader {
       GWT.create(TopConversationViewBuilder.Resources.class);
   private final static ParticipantsViewBuilder.Resources participants =
       GWT.create(ParticipantsViewBuilder.Resources.class);
+  private final static Dialog.Resources dialog =
+      GWT.create(Dialog.Resources.class);
 
   private final static BlipMessages blipMessages =
       GWT.create(BlipMessages.class);
@@ -70,6 +73,7 @@ public final class WavePanelResourceLoader {
     StyleInjector.inject(inlineContinuation.css().getText(), isSynchronous);
     StyleInjector.inject(conversation.css().getText(), isSynchronous);
     StyleInjector.inject(participants.css().getText(), isSynchronous);
+    StyleInjector.inject(dialog.css().getText(), isSynchronous);
   }
 
   private WavePanelResourceLoader() {
@@ -103,6 +107,10 @@ public final class WavePanelResourceLoader {
     return participants;
   }
 
+  public static Dialog.Resources getDialog() {
+    return dialog;
+  }
+
   public static BlipMessages getBlipMessages() {
     return blipMessages;
   }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/widget/dialog/Dialog.css
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/widget/dialog/Dialog.css 
b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.css
new file mode 100644
index 0000000..874ad37
--- /dev/null
+++ b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.css
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+.errorLabel {
+  color: red;
+  font-weight: bold;
+  padding: 5px;
+  text-align: center;
+}
+
+.warningLabel {
+  color: red;
+  font-weight: bold;
+  padding: 5px;
+  text-align: center;
+}
+
+.infoLabel {
+  font-weight: bold;
+  padding: 5px;
+  text-align: center;
+}
+
+.dialogButtonPanel {
+  padding: 5px;
+}
+
+.dialogButton {
+  min-width: 80px;
+  margin: 5px;
+}
+
+.glassPanel {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  opacity: 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/widget/dialog/Dialog.gwt.xml
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/widget/dialog/Dialog.gwt.xml 
b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.gwt.xml
new file mode 100644
index 0000000..8dbf371
--- /dev/null
+++ b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.gwt.xml
@@ -0,0 +1,6 @@
+<module>
+  <inherits name="com.google.gwt.user.User" />
+  <inherits name="org.waveprotocol.wave.client.widget.popup.Popup" />
+  <inherits name="org.waveprotocol.wave.client.widget.common.Common" />
+  <source path=""/>
+</module>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/widget/dialog/Dialog.java
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/widget/dialog/Dialog.java 
b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.java
new file mode 100644
index 0000000..b73d359
--- /dev/null
+++ b/src/org/waveprotocol/wave/client/widget/dialog/Dialog.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.wave.client.widget.dialog;
+
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.CssResource;
+import 
org.waveprotocol.wave.client.wavepanel.view.dom.full.WavePanelResourceLoader;
+
+/**
+ * Resources and common functions library for dialog widgets.
+ *
+ * @author Denis Konovalchik ([email protected])
+ */
+public class Dialog {
+  /** Resources used by this widget. */
+  public interface Resources extends ClientBundle {
+    @ClientBundle.Source("Dialog.css")
+    Css css();
+  }
+
+  /** CSS for this widget. */
+  public interface Css extends CssResource {
+    String errorLabel();
+    String warningLabel();
+    String infoLabel();
+    String dialogButtonPanel();
+    String dialogButton();
+    String glassPanel();
+  }
+
+  private static Css css;
+
+  public static Css getCss() {
+    if (css == null) {
+      css = WavePanelResourceLoader.getDialog().css();
+    }
+    return css;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/src/org/waveprotocol/wave/client/widget/dialog/DialogBox.java
----------------------------------------------------------------------
diff --git a/src/org/waveprotocol/wave/client/widget/dialog/DialogBox.java 
b/src/org/waveprotocol/wave/client/widget/dialog/DialogBox.java
new file mode 100644
index 0000000..4a8a61c
--- /dev/null
+++ b/src/org/waveprotocol/wave/client/widget/dialog/DialogBox.java
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.waveprotocol.wave.client.widget.dialog;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+import org.waveprotocol.wave.client.widget.popup.UniversalPopup;
+
+/**
+ * Standard dialog box with title, message and set of buttons.
+ *
+ * @author Denis Konovalchik ([email protected])
+ * @author [email protected] (A. Kaplanov)
+ */
+public class DialogBox {
+
+  public static class DialogButton {
+    private String title;
+    private Command onClick;
+    private Button button;
+
+    public DialogButton(String title) {
+      this.title = title;
+    }
+
+    public DialogButton(String title, Command onClick) {
+      this.title = title;
+      this.onClick = onClick;
+    }
+
+    public void setTitle(String title) {
+      this.title = title;
+      if (button != null) {
+        button.setText(title);
+      }
+    }
+
+    public void setOnClick(Command onClick) {
+      this.onClick = onClick;
+    }
+
+    public String getTitle() {
+      return title;
+    }
+
+    public void execute() {
+      if (onClick != null) {
+        onClick.execute();
+      }
+    }
+
+    void link(Button button) {
+      this.button = button;
+      button.setText(title);
+      button.addClickHandler(new ClickHandler() {
+
+        @Override
+        public void onClick(ClickEvent event) {
+          execute();
+        }
+      });
+    }
+  }
+
+  /**
+   * Creates dialog box.
+   *
+   * @param popup - UniversalPopup on which the dialog is based
+   * @param title - title placed in the title bar
+   * @param innerWidget - the inner widget of the dialog
+   * @param dialogButtons - buttons
+   */
+  static public void create(UniversalPopup popup, String title, Widget 
innerWidget, DialogButton[] dialogButtons) {
+    // Title
+    popup.getTitleBar().setTitleText(title);
+
+    VerticalPanel contents = new VerticalPanel();
+    popup.add(contents);
+
+    // Message
+    contents.add(innerWidget);
+
+    // Buttons
+    HorizontalPanel buttonPanel = new HorizontalPanel();
+    for(DialogButton dialogButton : dialogButtons) {
+      Button button = new Button(dialogButton.getTitle());
+      button.setStyleName(Dialog.getCss().dialogButton());
+      buttonPanel.add(button);
+      dialogButton.link(button);
+    }
+    contents.add(buttonPanel);
+    buttonPanel.setStyleName(Dialog.getCss().dialogButtonPanel());
+    contents.setCellHorizontalAlignment(buttonPanel, 
HasHorizontalAlignment.ALIGN_RIGHT);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/test/org/waveprotocol/box/server/rpc/RpcTest.java
----------------------------------------------------------------------
diff --git a/test/org/waveprotocol/box/server/rpc/RpcTest.java 
b/test/org/waveprotocol/box/server/rpc/RpcTest.java
index 689d5c6..ca28bff 100644
--- a/test/org/waveprotocol/box/server/rpc/RpcTest.java
+++ b/test/org/waveprotocol/box/server/rpc/RpcTest.java
@@ -46,6 +46,7 @@ import java.net.InetSocketAddress;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.waveprotocol.box.server.CoreSettings;
@@ -74,7 +75,8 @@ public class RpcTest extends TestCase {
      */
     server =
         new ServerRpcProvider(new InetSocketAddress[] {new 
InetSocketAddress("localhost", 0)},
-            new String[] {"./war"}, sessionManager, null, null, false, null, 
null);
+            new String[] {"./war"}, sessionManager, null, null, false, null, 
null,
+            Executors.newCachedThreadPool());
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d8896276/test/org/waveprotocol/box/server/waveserver/LucenePerUserWaveViewProviderTest.java
----------------------------------------------------------------------
diff --git 
a/test/org/waveprotocol/box/server/waveserver/LucenePerUserWaveViewProviderTest.java
 
b/test/org/waveprotocol/box/server/waveserver/LucenePerUserWaveViewProviderTest.java
index b4d02e4..078203c 100644
--- 
a/test/org/waveprotocol/box/server/waveserver/LucenePerUserWaveViewProviderTest.java
+++ 
b/test/org/waveprotocol/box/server/waveserver/LucenePerUserWaveViewProviderTest.java
@@ -29,6 +29,7 @@ import 
org.waveprotocol.box.server.persistence.lucene.RAMIndexDirectory;
 import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
 
 import java.io.IOException;
+import java.util.concurrent.Executors;
 
 /**
  * @author [email protected] (Yuri Zelikov)
@@ -57,7 +58,8 @@ public class LucenePerUserWaveViewProviderTest extends 
PerUserWaveViewProviderTe
   @Override
   protected PerUserWaveViewHandler createPerUserWaveViewHandler() {
     handler =
-        new LucenePerUserWaveViewHandlerImpl(directory, waveletProvider, 
textCollator, DOMAIN);
+        new LucenePerUserWaveViewHandlerImpl(directory, waveletProvider, 
textCollator, DOMAIN,
+          Executors.newCachedThreadPool());
     return handler;
   }
 

Reply via email to