Revert DeferredCommand deprecation due to reports of test failures.

Review by: rjrjr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8514 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
index 422743c..c199339 100644
--- a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
+++ b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
@@ -16,11 +16,11 @@
 package com.google.gwt.jsonp.client;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.ScriptElement;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Timer;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
@@ -246,7 +246,7 @@
      * scope of the script itself. Therefore, we need to defer the delete
      * statement after the callback execution.
      */
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         unregisterCallbacks(CALLBACKS);
         Node script = Document.get().getElementById(callbackId);
diff --git a/user/src/com/google/gwt/user/client/Command.java b/user/src/com/google/gwt/user/client/Command.java
index e97f5bf..90b9bde 100644
--- a/user/src/com/google/gwt/user/client/Command.java
+++ b/user/src/com/google/gwt/user/client/Command.java
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.user.client;
 
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-
 /**
  * Encapsulates an action for later execution, often from a different context.
  * 
@@ -24,16 +22,14 @@
  * The Command interface provides a layer of separation between the code
  * specifying some behavior and the code invoking that behavior. This separation
  * aids in creating reusable code. For example, a
- * {@link com.google.gwt.user.client.ui.MenuItem} can have a Command associated
- * with it that it executes when the menu item is chosen by the user.
+ * {@link com.google.gwt.user.client.ui.MenuItem} can have a Command
+ * associated with it that it executes when the menu item is chosen by the user.
  * Importantly, the code that constructed the Command to be executed when the
  * menu item is invoked knows nothing about the internals of the MenuItem class
- * and vice-versa.
- * </p>
+ * and vice-versa.</p>
  * 
- * <p>
- * The Command interface is often implemented with an anonymous inner class. For
- * example,
+ * <p> The Command interface is often implemented with an anonymous inner class.
+ * For example,
  * 
  * <pre>
  * Command sayHello = new Command() {
@@ -46,17 +42,10 @@
  * 
  * </p>
  */
-public interface Command extends ScheduledCommand {
-  /*
-   * NB: This extends ScheduledCommand to aid in transitioning from the old
-   * DeferredCommand interface. We can't have a class in core depend on a class
-   * in user, but we'd like switching to the Scheduler API to be pretty much
-   * just a name change. This type isn't deprecated, because it's used by other
-   * UI widget classes.
-   */
+public interface Command {
 
   /**
    * Causes the Command to perform its encapsulated behavior.
-   * */
+   */
   void execute();
 }
diff --git a/user/src/com/google/gwt/user/client/CommandExecutor.java b/user/src/com/google/gwt/user/client/CommandExecutor.java
new file mode 100644
index 0000000..8db1fe2
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/CommandExecutor.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * 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.google.gwt.user.client;
+
+import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Class which executes {@link Command}s and {@link IncrementalCommand}s after
+ * all currently pending event handlers have completed. This class attempts to
+ * protect against slow script warnings by running commands in small time
+ * increments.
+ * 
+ * <p>
+ * It is still possible that a poorly written command could cause a slow script
+ * warning which a user may choose to cancel. In that event, a
+ * {@link CommandCanceledException} or an
+ * {@link IncrementalCommandCanceledException} is reported through the current
+ * {@link UncaughtExceptionHandler} depending on the type of command which
+ * caused the warning. All other commands will continue to be executed.
+ * </p>
+ * 
+ * TODO(mmendez): Can an SSW be detected without using a timer? Currently, if a
+ * {@link Command} or an {@link IncrementalCommand} calls either
+ * {@link Window#alert(String)} or the JavaScript <code>alert(String)</code>
+ * methods directly or indirectly then the  cancellation timer can fire,
+ * resulting in a false SSW cancellation detection.
+ */
+class CommandExecutor {
+
+  /**
+   * A circular iterator used by this class. This iterator will wrap back to
+   * zero when it hits the end of the commands.
+   */
+  private class CircularIterator implements Iterator<Object> {
+    /**
+     * Index of the element where this iterator should wrap back to the
+     * beginning of the collection.
+     */
+    private int end;
+
+    /**
+     * Index of the last item returned by {@link #next()}.
+     */
+    private int last = -1;
+
+    /**
+     * Index of the next command to execute.
+     */
+    private int next = 0;
+
+    /**
+     * Returns <code>true</code> if there are more commands in the queue.
+     * 
+     * @return <code>true</code> if there are more commands in the queue.
+     */
+    public boolean hasNext() {
+      return next < end;
+    }
+
+    /**
+     * Returns the next command from the queue. When the end of the dispatch
+     * region is reached it will wrap back to the start.
+     * 
+     * @return next command from the queue.
+     */
+    public Object next() {
+      last = next;
+      Object command = commands.get(next++);
+      if (next >= end) {
+        next = 0;
+      }
+
+      return command;
+    }
+
+    /**
+     * Removes the command which was previously returned by {@link #next()}.
+     * 
+     */
+    public void remove() {
+      assert (last >= 0);
+
+      commands.remove(last);
+      --end;
+
+      if (last <= next) {
+        if (--next < 0) {
+          next = 0;
+        }
+      }
+
+      last = -1;
+    }
+
+    /**
+     * Returns the last element returned by {@link #next()}.
+     * 
+     * @return last element returned by {@link #next()}
+     */
+    private Object getLast() {
+      assert (last >= 0);
+      return commands.get(last);
+    }
+
+    private void setEnd(int end) {
+      assert (end >= next);
+
+      this.end = end;
+    }
+
+    private void setLast(int last) {
+      this.last = last;
+    }
+
+    private boolean wasRemoved() {
+      return last == -1;
+    }
+  }
+
+  /**
+   * Default amount of time to wait before assuming that a script cancellation
+   * has taken place. This should be a platform dependent value, ultimately we
+   * may need to acquire this value based on a rebind decision. For now, we
+   * chose the smallest value known to cause an SSW.
+   */
+  private static final int DEFAULT_CANCELLATION_TIMEOUT_MILLIS = 10000;
+
+  /**
+   * Default amount of time to spend dispatching commands before we yield to the
+   * system.
+   */
+  private static final int DEFAULT_TIME_SLICE_MILLIS = 100;
+
+  /**
+   * Returns true the end time has been reached or exceeded.
+   * 
+   * @param currentTimeMillis current time in milliseconds
+   * @param startTimeMillis end time in milliseconds
+   * @return true if the end time has been reached
+   */
+  private static boolean hasTimeSliceExpired(double currentTimeMillis,
+      double startTimeMillis) {
+    return currentTimeMillis - startTimeMillis >= DEFAULT_TIME_SLICE_MILLIS;
+  }
+
+  /**
+   * Timer used to recover from script cancellations arising from slow script
+   * warnings.
+   */
+  private final Timer cancellationTimer = new Timer() {
+    @Override
+    public void run() {
+      if (!isExecuting()) {
+        /*
+         * If we are not executing, then the cancellation timer expired right
+         * about the time that the command dispatcher finished -- we are okay so
+         * we just exit.
+         */
+        return;
+      }
+
+      doCommandCanceled();
+    }
+  };
+
+  /**
+   * Commands that need to be executed.
+   */
+  private final List<Object> commands = new ArrayList<Object>();
+
+  /**
+   * Set to <code>true</code> when we are actively dispatching commands.
+   */
+  private boolean executing = false;
+
+  /**
+   * Timer used to drive the dispatching of commands in the background.
+   */
+  private final Timer executionTimer = new Timer() {
+    @Override
+    public void run() {
+      assert (!isExecuting());
+
+      setExecutionTimerPending(false);
+
+      doExecuteCommands(Duration.currentTimeMillis());
+    }
+  };
+
+  /**
+   * Set to <code>true</code> when we are waiting for a dispatch timer event
+   * to fire.
+   */
+  private boolean executionTimerPending = false;
+
+  /**
+   * The single circular iterator instance that we use to iterate over the
+   * collection of commands.
+   */
+  private final CircularIterator iterator = new CircularIterator();
+
+  /**
+   * Submits a {@link Command} for execution.
+   * 
+   * @param command command to submit
+   */
+  public void submit(Command command) {
+    commands.add(command);
+
+    maybeStartExecutionTimer();
+  }
+
+  /**
+   * Submits an {@link IncrementalCommand} for execution.
+   * 
+   * @param command command to submit
+   */
+  public void submit(IncrementalCommand command) {
+    commands.add(command);
+
+    maybeStartExecutionTimer();
+  }
+
+  /**
+   * Reports either a {@link CommandCanceledException} or an
+   * {@link IncrementalCommandCanceledException} back through the
+   * {@link UncaughtExceptionHandler} if one is set.
+   */
+  protected void doCommandCanceled() {
+    Object cmd = iterator.getLast();
+    iterator.remove();
+    assert (cmd != null);
+
+    RuntimeException ex = null;
+    if (cmd instanceof Command) {
+      ex = new CommandCanceledException((Command) cmd);
+    } else if (cmd instanceof IncrementalCommand) {
+      ex = new IncrementalCommandCanceledException((IncrementalCommand) cmd);
+    }
+
+    if (ex != null) {
+      UncaughtExceptionHandler ueh = GWT.getUncaughtExceptionHandler();
+      if (ueh != null) {
+        ueh.onUncaughtException(ex);
+      }
+    }
+
+    setExecuting(false);
+
+    maybeStartExecutionTimer();
+  }
+
+  /**
+   * This method will dispatch commands from the command queue. It will dispatch
+   * commands until one of the following conditions is <code>true</code>:
+   * <ul>
+   * <li>It consumed its dispatching time slice
+   * {@value #DEFAULT_TIME_SLICE_MILLIS}</li>
+   * <li>It encounters a <code>null</code> in the command queue</li>
+   * <li>All commands which were present at the start of the dispatching have
+   * been removed from the command queue</li>
+   * <li>The command that it was processing was canceled due to a false
+   * cancellation -- in this case we exit without updating any state</li>
+   * </ul>
+   * 
+   * @param startTimeMillis the time when this method started
+   */
+  protected void doExecuteCommands(double startTimeMillis) {
+    assert (!isExecutionTimerPending());
+
+    boolean wasCanceled = false;
+    try {
+      setExecuting(true);
+
+      iterator.setEnd(commands.size());
+
+      cancellationTimer.schedule(DEFAULT_CANCELLATION_TIMEOUT_MILLIS);
+
+      while (iterator.hasNext()) {
+        Object element = iterator.next();
+
+        boolean removeCommand = true;
+        try {
+          if (element == null) {
+            // null forces a yield or pause in execution
+            return;
+          }
+
+          if (element instanceof Command) {
+            Command command = (Command) element;
+            command.execute();
+          } else if (element instanceof IncrementalCommand) {
+            IncrementalCommand incrementalCommand = (IncrementalCommand) element;
+            removeCommand = !incrementalCommand.execute();
+          }
+
+        } finally {
+          wasCanceled = iterator.wasRemoved();
+          if (!wasCanceled) {
+            if (removeCommand) {
+              iterator.remove();
+            }
+          }
+        }
+
+        if (hasTimeSliceExpired(Duration.currentTimeMillis(), startTimeMillis)) {
+          // the time slice has expired
+          return;
+        }
+      }
+    } finally {
+      if (!wasCanceled) {
+        cancellationTimer.cancel();
+
+        setExecuting(false);
+
+        maybeStartExecutionTimer();
+      }
+    }
+  }
+
+  /**
+   * Starts the dispatch timer if there are commands to dispatch and we are not
+   * waiting for a dispatch timer and we are not actively dispatching.
+   */
+  protected void maybeStartExecutionTimer() {
+    if (!commands.isEmpty() && !isExecutionTimerPending() && !isExecuting()) {
+      setExecutionTimerPending(true);
+      executionTimer.schedule(1);
+    }
+  }
+
+  /**
+   * This method is for testing only.
+   */
+  List<Object> getPendingCommands() {
+    return commands;
+  }
+
+  /**
+   * This method is for testing only.
+   */
+  void setExecuting(boolean executing) {
+    this.executing = executing;
+  }
+
+  /**
+   * This method is for testing only.
+   */
+  void setLast(int last) {
+    iterator.setLast(last);
+  }
+
+  /**
+   * Returns <code>true</code> if this instance is currently dispatching
+   * commands.
+   * 
+   * @return <code>true</code> if this instance is currently dispatching
+   *         commands
+   */
+  private boolean isExecuting() {
+    return executing;
+  }
+
+  /**
+   * Returns <code>true</code> if a the dispatch timer was scheduled but it
+   * still has not fired.
+   * 
+   * @return <code>true</code> if a the dispatch timer was scheduled but it
+   *         still has not fired
+   */
+  private boolean isExecutionTimerPending() {
+    return executionTimerPending;
+  }
+
+  private void setExecutionTimerPending(boolean pending) {
+    executionTimerPending = pending;
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/client/DeferredCommand.java b/user/src/com/google/gwt/user/client/DeferredCommand.java
index c7ed6eb..9fe21d4 100644
--- a/user/src/com/google/gwt/user/client/DeferredCommand.java
+++ b/user/src/com/google/gwt/user/client/DeferredCommand.java
@@ -15,41 +15,14 @@
  */
 package com.google.gwt.user.client;
 
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * This class allows you to execute code after all currently pending event
  * handlers have completed, using the {@link #addCommand(Command)} or
  * {@link #addCommand(IncrementalCommand)} methods. This is useful when you need
  * to execute code outside of the context of the current stack.
- * 
- * @deprecated Replaced by {@link Scheduler}
  */
-@Deprecated
 public class DeferredCommand {
-  private static class PausedCommand implements ScheduledCommand {
-    List<ScheduledCommand> toSchedule = new ArrayList<ScheduledCommand>();
-    PausedCommand next;
-
-    public void execute() {
-      if (lastPaused == this) {
-        lastPaused = null;
-      }
-      for (ScheduledCommand cmd : toSchedule) {
-        Scheduler.get().scheduleDeferred(cmd);
-      }
-      if (next != null) {
-        // Scheduled a chained pause
-        Scheduler.get().scheduleDeferred(next);
-      }
-    }
-  }
-
-  private static PausedCommand lastPaused;
+  private static final CommandExecutor commandExecutor = new CommandExecutor();
 
   /**
    * Enqueues a {@link Command} to be fired after all current events have been
@@ -59,14 +32,12 @@
    *          inserted into the queue. Any events added after the pause will
    *          wait for an additional cycle through the system event loop before
    *          executing. Pauses are cumulative.
+   * 
    * @deprecated As of release 1.4, replaced by {@link #addCommand(Command)}
    */
+  @Deprecated
   public static void add(Command cmd) {
-    if (cmd == null) {
-      addPause();
-    } else {
-      addCommand(cmd);
-    }
+    commandExecutor.submit(cmd);
   }
 
   /**
@@ -77,19 +48,13 @@
    * 
    * @param cmd the command to be fired
    * @throws NullPointerException if cmd is <code>null</code>
-   * @deprecated Replaced by
-   *             {@link Scheduler#scheduleDeferred(ScheduledCommand)}
    */
   public static void addCommand(Command cmd) {
     if (cmd == null) {
       throw new NullPointerException("cmd cannot be null");
     }
 
-    if (lastPaused != null) {
-      lastPaused.toSchedule.add(cmd);
-    } else {
-      Scheduler.get().scheduleDeferred(cmd);
-    }
+    commandExecutor.submit(cmd);
   }
 
   /**
@@ -101,14 +66,13 @@
    * 
    * @param cmd the command to be fired
    * @throws NullPointerException if cmd is <code>null</code>
-   * @deprecated Replaced by {@link Scheduler#scheduleIncremental}
    */
   public static void addCommand(IncrementalCommand cmd) {
     if (cmd == null) {
       throw new NullPointerException("cmd cannot be null");
     }
 
-    Scheduler.get().scheduleIncremental(cmd);
+    commandExecutor.submit(cmd);
   }
 
   /**
@@ -116,20 +80,8 @@
    * {@link DeferredCommand}s or pauses that are added after this pause will
    * wait for an additional cycle through the system event loop before
    * executing.
-   * 
-   * @deprecated No direct replacement; instead, a ScheduledCommand should cause
-   *             any other strictly-ordered commands to be scheduled
    */
   public static void addPause() {
-    if (lastPaused == null) {
-      // No existing pause
-      lastPaused = new PausedCommand();
-      Scheduler.get().scheduleDeferred(lastPaused);
-    } else {
-      // Chained pauses
-      PausedCommand newPaused = new PausedCommand();
-      lastPaused.next = newPaused;
-      lastPaused = newPaused;
-    }
+    commandExecutor.submit((Command) null);
   }
 }
diff --git a/user/src/com/google/gwt/user/client/IncrementalCommand.java b/user/src/com/google/gwt/user/client/IncrementalCommand.java
index 4d18b32..d2f9739 100644
--- a/user/src/com/google/gwt/user/client/IncrementalCommand.java
+++ b/user/src/com/google/gwt/user/client/IncrementalCommand.java
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.user.client;
 
-import com.google.gwt.core.client.Scheduler;
-
 /**
  * An <code>IncrementalCommand</code> is a command that is broken into one or
  * more substeps, each substep brings the whole command nearer to completion.
@@ -24,12 +22,8 @@
  * <code>false</code>.
  * 
  * {@example com.google.gwt.examples.IncrementalCommandExample}
- * 
- * @deprecated Replaced by {@link Scheduler.RepeatingCommand} for use with
- *             {@link Scheduler#scheduleIncremental}
  */
-@Deprecated
-public interface IncrementalCommand extends Scheduler.RepeatingCommand {
+public interface IncrementalCommand {
   /**
    * Causes the <code>IncrementalCommand</code> to execute its encapsulated
    * behavior.
diff --git a/user/src/com/google/gwt/user/client/ui/CaptionPanel.java b/user/src/com/google/gwt/user/client/ui/CaptionPanel.java
index bacaaf1..4364b2b 100644
--- a/user/src/com/google/gwt/user/client/ui/CaptionPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/CaptionPanel.java
@@ -16,12 +16,12 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.FieldSetElement;
 import com.google.gwt.dom.client.LegendElement;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 import java.util.Iterator;
 
@@ -80,7 +80,7 @@
         boolean asHTML) {
       fieldset.getStyle().setProperty("visibility", "hidden");
       super.setCaption(fieldset, legend, caption, asHTML);
-      Scheduler.get().scheduleDeferred(new Command() {
+      DeferredCommand.addCommand(new Command() {
         public void execute() {
           fieldset.getStyle().setProperty("visibility", "");
         }
diff --git a/user/src/com/google/gwt/user/client/ui/FormPanel.java b/user/src/com/google/gwt/user/client/ui/FormPanel.java
index f3c4941..2ad6825 100644
--- a/user/src/com/google/gwt/user/client/ui/FormPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FormPanel.java
@@ -16,7 +16,6 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.FormElement;
@@ -24,6 +23,7 @@
 import com.google.gwt.event.shared.GwtEvent;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.ui.impl.FormPanelImpl;
 import com.google.gwt.user.client.ui.impl.FormPanelImplHost;
@@ -627,7 +627,7 @@
     // because clients that detach the form panel when submission is
     // complete can cause some browsers (i.e. Mozilla) to go into an
     // 'infinite loading' state. See issue 916.
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         fireEvent(new SubmitCompleteEvent(impl.getContents(synthesizedFrame)));
       }
diff --git a/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java b/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
index 09cd37a..c4b9157 100644
--- a/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
@@ -16,12 +16,12 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.resources.client.ClientBundle;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Timer;
 
@@ -301,7 +301,7 @@
           // before layout completes, the RIGHT element will
           // appear to be blanked out.
 
-          Scheduler.get().scheduleDeferred(new Command() {
+          DeferredCommand.addCommand(new Command() {
             public void execute() {
               setWidth(panel.getElement(LEFT), "0px");
             }
@@ -578,7 +578,7 @@
      * possible.
      */
     setSplitPosition(lastSplitPosition);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         setSplitPosition(lastSplitPosition);
       }
diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java
index b7d7ec9..8d85c76 100644
--- a/user/src/com/google/gwt/user/client/ui/Image.java
+++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -16,7 +16,6 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.ImageElement;
@@ -46,6 +45,7 @@
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.ui.impl.ClippedImageImpl;
 
@@ -258,7 +258,7 @@
        * that a second load event would occur while you are in the load event
        * handler.
        */
-      Scheduler.get().scheduleDeferred(new Command() {
+      DeferredCommand.addCommand(new Command() {
         public void execute() {
           NativeEvent evt = Document.get().createLoadEvent();
           getImageElement(image).dispatchEvent(evt);
diff --git a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
index 352c815..b7277d7 100644
--- a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
@@ -15,10 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Event;
 
 /**
@@ -170,7 +170,7 @@
             forceLayout();
           }
         };
-        Scheduler.get().scheduleDeferred(layoutCommand);
+        DeferredCommand.addCommand(layoutCommand);
       }
     }
   }
diff --git a/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java b/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
index b655644..c3f720d 100644
--- a/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
@@ -16,11 +16,11 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.resources.client.ClientBundle;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Timer;
 
@@ -397,7 +397,7 @@
      * This first call is simply to try to avoid a jitter effect if possible.
      */
     setSplitPosition(lastSplitPosition);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         setSplitPosition(lastSplitPosition);
       }
diff --git a/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java b/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
index 0d9c16a..0c1dfef 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
@@ -15,12 +15,12 @@
  */
 package com.google.gwt.user.client.ui.impl;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Overflow;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Implementation class used by {@link com.google.gwt.user.client.ui.PopupPanel}.
@@ -99,7 +99,7 @@
       // 'overflow:auto' after all of the elements on the page have been
       // rendered,
       // the PopupPanel becomes the highest element in the stacking order.
-      Scheduler.get().scheduleDeferred(new Command() {
+      DeferredCommand.addCommand(new Command() {
         public void execute() {
           outerElem.getStyle().setOverflow(Overflow.AUTO);
         }
diff --git a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOldMozilla.java b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOldMozilla.java
index 548e928..d78a4e6 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOldMozilla.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/RichTextAreaImplOldMozilla.java
@@ -15,8 +15,8 @@
  */
 package com.google.gwt.user.client.ui.impl;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Old Mozilla-specific implementation of rich-text editing.
@@ -28,7 +28,7 @@
    */
   @Override
   protected void onElementInitialized() {
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         RichTextAreaImplOldMozilla.super.onElementInitialized();
       }
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
index d060235..7105c80 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -17,7 +17,6 @@
 package com.google.gwt.user.datepicker.client;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.event.dom.client.BlurEvent;
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.event.dom.client.ClickEvent;
@@ -34,6 +33,7 @@
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.i18n.client.DateTimeFormat;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.HasValue;
 import com.google.gwt.user.client.ui.PopupPanel;
@@ -458,7 +458,7 @@
 
   private void preventDatePickerPopup() {
     allowDPShow = false;
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         allowDPShow = true;
       }
diff --git a/user/src/com/google/gwt/view/client/ListViewAdapter.java b/user/src/com/google/gwt/view/client/ListViewAdapter.java
index 9ccb78e..3f0ac0c 100644
--- a/user/src/com/google/gwt/view/client/ListViewAdapter.java
+++ b/user/src/com/google/gwt/view/client/ListViewAdapter.java
@@ -15,8 +15,8 @@
  */
 package com.google.gwt.view.client;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -394,7 +394,7 @@
       flushCancelled = false;
       if (!flushPending) {
         flushPending = true;
-        Scheduler.get().scheduleDeferred(flushCommand);
+        DeferredCommand.addCommand(flushCommand);
       }
     }
 
diff --git a/user/test/com/google/gwt/dev/jjs/test/RunAsyncMetricsIntegrationTest.java b/user/test/com/google/gwt/dev/jjs/test/RunAsyncMetricsIntegrationTest.java
index fa8136e..37085f6 100644
--- a/user/test/com/google/gwt/dev/jjs/test/RunAsyncMetricsIntegrationTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/RunAsyncMetricsIntegrationTest.java
@@ -18,9 +18,9 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.RunAsyncCallback;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 import java.util.LinkedList;
 import java.util.Queue;
@@ -131,7 +131,7 @@
       }
 
       public void onSuccess() {
-        Scheduler.get().scheduleDeferred(new Command() {
+        DeferredCommand.addCommand(new Command() {
 
           public void execute() {
             checkMetrics();
diff --git a/user/test/com/google/gwt/dom/client/ElementTest.java b/user/test/com/google/gwt/dom/client/ElementTest.java
index 857dede..5806456 100644
--- a/user/test/com/google/gwt/dom/client/ElementTest.java
+++ b/user/test/com/google/gwt/dom/client/ElementTest.java
@@ -16,13 +16,13 @@
 package com.google.gwt.dom.client;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Style.Position;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Element tests (many stolen from DOMTest).
@@ -201,7 +201,7 @@
     elem.getStyle().setPropertyPx("width", width);
     elem.getStyle().setPropertyPx("height", height);
 
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         int absLeft = left + margin;
         int absTop = top + margin;
diff --git a/user/test/com/google/gwt/dom/client/StyleInjectorTest.java b/user/test/com/google/gwt/dom/client/StyleInjectorTest.java
index c9a217f..be61d38 100644
--- a/user/test/com/google/gwt/dom/client/StyleInjectorTest.java
+++ b/user/test/com/google/gwt/dom/client/StyleInjectorTest.java
@@ -15,9 +15,9 @@
  */
 package com.google.gwt.dom.client;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests StyleInjector by looking for effects of injected CSS on DOM elements.
@@ -45,7 +45,7 @@
     // We need to allow the document to be redrawn
     delayTestFinish(TEST_DELAY);
 
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         assertEquals(100, elt.getOffsetLeft());
         assertEquals(100, elt.getClientHeight());
@@ -80,7 +80,7 @@
     // We need to allow the document to be redrawn
     delayTestFinish(TEST_DELAY);
 
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         assertEquals(100, elt.getOffsetLeft());
         assertEquals(100, elt.getClientHeight());
@@ -131,7 +131,7 @@
     if (immediate) {
       command.execute();
     } else {
-      Scheduler.get().scheduleDeferred(command);
+      DeferredCommand.addCommand(command);
       // We need to allow the BatchedCommands to execute
       delayTestFinish(TEST_DELAY);
     }
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 0940f39..06960cc 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -18,6 +18,7 @@
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.layout.client.LayoutTest;
 import com.google.gwt.user.client.AsyncProxyTest;
+import com.google.gwt.user.client.CommandExecutorTest;
 import com.google.gwt.user.client.CookieTest;
 import com.google.gwt.user.client.EventTest;
 import com.google.gwt.user.client.HistoryDisabledTest;
@@ -116,6 +117,7 @@
     suite.addTestSuite(CaptionPanelTest.class);
     suite.addTestSuite(CheckBoxTest.class);
     suite.addTestSuite(ClippedImagePrototypeTest.class);
+    suite.addTestSuite(CommandExecutorTest.class);
     suite.addTestSuite(CompositeTest.class);
     suite.addTestSuite(CookieTest.class);
     suite.addTestSuite(CustomButtonTest.class);
diff --git a/user/test/com/google/gwt/user/client/CommandExecutorTest.java b/user/test/com/google/gwt/user/client/CommandExecutorTest.java
new file mode 100644
index 0000000..1e970d9
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/CommandExecutorTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * 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.google.gwt.user.client;
+
+import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Test cases for {@link CommandExecutor}.
+ */
+public class CommandExecutorTest extends GWTTestCase {
+
+  private static class NonRestartingCommandExecutor extends CommandExecutor {
+    protected void maybeStartExecutionTimer() {
+      // keeps the executing timer for interfering with the test
+    }
+  }
+
+  private static class TestCommand implements Command {
+    private boolean executed;
+
+    public boolean didExecute() {
+      return executed;
+    }
+
+    public void execute() {
+      executed = true;
+    }
+  }
+
+  private static class TestIncrementalCommand implements IncrementalCommand {
+    private boolean done = false;
+    private int executeCount;
+
+    public boolean execute() {
+      ++executeCount;
+
+      return !isDone();
+    }
+
+    public int getExecuteCount() {
+      return executeCount;
+    }
+
+    public boolean isDone() {
+      return done;
+    }
+
+    public void setDone(boolean done) {
+      this.done = done;
+    }
+  }
+
+  /**
+   * A sufficiently large delay to let the SSW triggers.
+   */
+  private static final int TEST_FINISH_DELAY_MILLIS = 40000;
+
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks that we can recover after a cancellation
+   */
+  public void testDoExecuteCommands_CancellationRecovery() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    final Command c1 = new Command() {
+      public void execute() {
+      }
+    };
+
+    ce.setExecuting(true);
+    ce.submit(c1);
+    ce.setLast(0);
+
+    final UncaughtExceptionHandler originalUEH = GWT.getUncaughtExceptionHandler();
+
+    UncaughtExceptionHandler ueh1 = new UncaughtExceptionHandler() {
+      public void onUncaughtException(Throwable e) {
+        if (!(e instanceof CommandCanceledException)) {
+          originalUEH.onUncaughtException(e);
+          return;
+        }
+
+        CommandCanceledException cce = (CommandCanceledException) e;
+        if (cce.getCommand() != c1) {
+          fail("CommandCanceledException did not contain the correct failed command");
+        }
+
+        // Submit some more work and do another dispatch
+        ce.submit(new IncrementalCommand() {
+          public boolean execute() {
+            return false;
+          }
+        });
+
+        delayTestFinish(TEST_FINISH_DELAY_MILLIS);
+        ce.submit(new Command() {
+          public void execute() {
+            finishTest();
+          }
+        });
+
+        ce.doExecuteCommands(Duration.currentTimeMillis());
+      }
+    };
+
+    GWT.setUncaughtExceptionHandler(ueh1);
+    ce.doCommandCanceled();
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks Command cancellation detection
+   */
+  public void testDoExecuteCommands_CommandCancellation() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    final Command c1 = new Command() {
+      public void execute() {
+      }
+    };
+
+    // Setup the cancellation state
+    ce.setExecuting(true);
+    ce.submit(c1);
+    ce.setLast(0);
+
+    final UncaughtExceptionHandler originalUEH = GWT.getUncaughtExceptionHandler();
+
+    UncaughtExceptionHandler ueh1 = new UncaughtExceptionHandler() {
+      public void onUncaughtException(Throwable e) {
+        if (!(e instanceof CommandCanceledException)) {
+          originalUEH.onUncaughtException(e);
+          return;
+        }
+
+        CommandCanceledException cce = (CommandCanceledException) e;
+        if (cce.getCommand() != c1) {
+          fail("CommandCanceledException did not contain the correct failed command");
+        }
+      }
+    };
+
+    GWT.setUncaughtExceptionHandler(ueh1);
+    ce.doCommandCanceled();
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks that calling {@link CommandExecutor#doExecuteCommands(double)} with no
+   * items in the queue is safe
+   */
+  public void testDoExecuteCommands_emptyQueue() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    ce.doExecuteCommands(Duration.currentTimeMillis());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks IncrementalCommand cancellation detection
+   */
+  public void testDoExecuteCommands_IncrementalCommandCancellation() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    final IncrementalCommand ic = new IncrementalCommand() {
+      public boolean execute() {
+        return false;
+      }
+    };
+
+    // setup the cancellation state
+    ce.setExecuting(true);
+    ce.submit(ic);
+    ce.setLast(0);
+
+    final UncaughtExceptionHandler originalUEH = GWT.getUncaughtExceptionHandler();
+
+    UncaughtExceptionHandler ueh1 = new UncaughtExceptionHandler() {
+      public void onUncaughtException(Throwable e) {
+        if (!(e instanceof IncrementalCommandCanceledException)) {
+          originalUEH.onUncaughtException(e);
+          return;
+        }
+
+        IncrementalCommandCanceledException icce = (IncrementalCommandCanceledException) e;
+        if (icce.getCommand() != ic) {
+          fail("IncrementalCommandCanceledException did not contain the correct failed command");
+        }
+      }
+    };
+
+    GWT.setUncaughtExceptionHandler(ueh1);
+    ce.doCommandCanceled();
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks that an incremental command executes and is removed from the queue
+   * when it is done
+   */
+  public void testDoExecuteCommands_IncrementalCommands() {
+    TestIncrementalCommand tic = new TestIncrementalCommand();
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    tic.setDone(true);
+    ce.submit(tic);
+    ce.doExecuteCommands(Duration.currentTimeMillis());
+    assertTrue(tic.getExecuteCount() > 0);
+    assertTrue(ce.getPendingCommands().isEmpty());
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks that null does in fact cause a pause.
+   */
+  public void testDoExecuteCommands_pause() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    TestCommand tc1 = new TestCommand();
+    TestCommand tc2 = new TestCommand();
+
+    ce.submit(tc1);
+    ce.submit((Command) null);
+    ce.submit(tc2);
+
+    ce.doExecuteCommands(Duration.currentTimeMillis());
+
+    assertTrue(tc1.didExecute() && !tc2.didExecute());
+    assertEquals(1, ce.getPendingCommands().size());
+    assertTrue(ce.getPendingCommands().contains(tc2));
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
+   * 
+   * Checks that after one pass dispatch pass, we still have the incremental
+   * command in the queue
+   */
+  public void testDoExecuteCommands_timeSliceUsage() {
+    final CommandExecutor ce = new NonRestartingCommandExecutor();
+
+    Command tc = new TestCommand();
+    ce.submit(tc);
+
+    TestIncrementalCommand tic = new TestIncrementalCommand();
+    ce.submit(tic);
+    ce.doExecuteCommands(Duration.currentTimeMillis());
+
+    assertEquals(1, ce.getPendingCommands().size());
+    assertTrue(ce.getPendingCommands().contains(tic));
+    assertTrue(tic.getExecuteCount() > 0);
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#submit(com.google.gwt.user.client.Command)}.
+   * 
+   * <p/> Cases:
+   * <ul>
+   * <li>Submit <code>null</code></li>
+   * <li>Submit {@link Command} and make sure that it fires</li>
+   * </ul>
+   */
+  public void testSubmitCommand() {
+    CommandExecutor ce = new CommandExecutor();
+    ce.submit((Command) null);
+
+    delayTestFinish(TEST_FINISH_DELAY_MILLIS);
+
+    ce.submit(new Command() {
+      public void execute() {
+        finishTest();
+      }
+    });
+  }
+
+  /**
+   * Test method for
+   * {@link com.google.gwt.user.client.CommandExecutor#submit(com.google.gwt.user.client.IncrementalCommand)}.
+   * 
+   * <p/> Cases:
+   * <ul>
+   * <li>Submit <code>null</code></li>
+   * <li>Submit {@link IncrementalCommand} and make sure that it fires as many
+   * times as we want it to</li>
+   * </ul>
+   */
+  public void testSubmitIncrementalCommand() {
+    CommandExecutor ce = new CommandExecutor();
+    ce.submit((Command) null);
+
+    delayTestFinish(TEST_FINISH_DELAY_MILLIS);
+
+    ce.submit(new IncrementalCommand() {
+      private int executionCount = 0;
+
+      public boolean execute() {
+        if (++executionCount > 10) {
+          fail("IncrementalCommand was fired more than 10 times");
+        }
+
+        if (executionCount == 10) {
+          finishTest();
+        }
+
+        return executionCount < 10;
+      }
+    });
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/WindowTest.java b/user/test/com/google/gwt/user/client/WindowTest.java
index 3361ba9..82e04da 100644
--- a/user/test/com/google/gwt/user/client/WindowTest.java
+++ b/user/test/com/google/gwt/user/client/WindowTest.java
@@ -16,7 +16,6 @@
 package com.google.gwt.user.client;
 
 import com.google.gwt.core.client.JavaScriptException;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.event.logical.shared.ResizeEvent;
 import com.google.gwt.event.logical.shared.ResizeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
@@ -220,7 +219,7 @@
     largeDOM.setPixelSize(oldClientWidth + 100, oldClientHeight + 100);
     RootPanel.get().add(largeDOM);
     delayTestFinish(200);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         int newClientHeight = Window.getClientHeight();
         int newClientWidth = Window.getClientWidth();
@@ -360,7 +359,7 @@
     final HandlerRegistration handlerRegistration = Window.addResizeHandler(resizeHandler);
 
     delayTestFinish(2000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         // Sizes must be appropriate, otherwise browsers may not resize as
         // requested. See comments in ResizeHelper.
diff --git a/user/test/com/google/gwt/user/client/ui/CompositeTest.java b/user/test/com/google/gwt/user/client/ui/CompositeTest.java
index 9e414f5..37abcf8 100644
--- a/user/test/com/google/gwt/user/client/ui/CompositeTest.java
+++ b/user/test/com/google/gwt/user/client/ui/CompositeTest.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.event.dom.client.BlurEvent;
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.event.dom.client.FocusEvent;
@@ -23,6 +22,7 @@
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Event;
 
 /**
@@ -100,9 +100,9 @@
     // Focus, then blur, the composite's text box. This has to be done in
     // deferred commands, because focus events usually require the event loop
     // to be pumped in order to fire.
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
-        Scheduler.get().scheduleDeferred(new Command() {
+        DeferredCommand.addCommand(new Command() {
           public void execute() {
             // Ensure all events fired as expected.
             assertTrue(c.domFocusFired);
diff --git a/user/test/com/google/gwt/user/client/ui/DOMTest.java b/user/test/com/google/gwt/user/client/ui/DOMTest.java
index 24b9e89..17e09b9 100644
--- a/user/test/com/google/gwt/user/client/ui/DOMTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DOMTest.java
@@ -16,7 +16,6 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
 import com.google.gwt.dom.client.BodyElement;
 import com.google.gwt.dom.client.Document;
@@ -28,6 +27,7 @@
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.Timer;
@@ -141,7 +141,7 @@
     DOM.setStyleAttribute(elem, "left", (left - doc.getBodyOffsetTop()) + "px");
 
     delayTestFinish(1000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         assertEquals(top + margin, DOM.getAbsoluteTop(elem));
         assertEquals(left + margin, DOM.getAbsoluteLeft(elem));
diff --git a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
index 4ecc0e4..d6603ae 100644
--- a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
@@ -15,13 +15,13 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Unit test for {@link DialogBox}.
@@ -103,7 +103,7 @@
 
     delayTestFinish(250);
     // Check the header IDs
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         UIObjectTest.assertDebugIdContents("myDialogBox-caption",
             "test caption");
diff --git a/user/test/com/google/gwt/user/client/ui/LayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/LayoutPanelTest.java
index 7e48183..6ac4e96 100644
--- a/user/test/com/google/gwt/user/client/ui/LayoutPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/LayoutPanelTest.java
@@ -15,12 +15,12 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.layout.client.Layout.AnimationCallback;
 import com.google.gwt.layout.client.Layout.Layer;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests for {@link LayoutPanel}. Note that this only tests LayoutPanel-specific
@@ -42,7 +42,7 @@
     p.forceLayout();
 
     delayTestFinish(5000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         p.animate(100, new AnimationCallback() {
           public void onLayout(Layer layer, double progress) {
@@ -73,7 +73,7 @@
     popup.center();
 
     delayTestFinish(2000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         int offsetWidth = lp.getOffsetWidth();
         int offsetHeight = lp.getOffsetHeight();
diff --git a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
index e9c6f44..eb06f9e 100644
--- a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
@@ -15,9 +15,9 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests {@link ListBox}. Needs many, many more tests.
@@ -50,7 +50,7 @@
     UIObjectTest.assertDebugId("myList", list.getElement());
 
     delayTestFinish(5000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         UIObjectTest.assertDebugIdContents("myList-item0", "option0");   
         UIObjectTest.assertDebugIdContents("myList-item1", "option1");   
diff --git a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
index bf4dccb..f08931f 100644
--- a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
+++ b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
@@ -15,13 +15,13 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 import java.util.List;
 
@@ -297,7 +297,7 @@
     UIObjectTest.assertDebugId("myMenu", bar.getElement());
 
     delayTestFinish(250);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         UIObjectTest.assertDebugIdContents("myMenu-item0", "top0");
         UIObjectTest.assertDebugIdContents("myMenu-item1", "top1");
diff --git a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
index ee4576d..4bf82e6 100644
--- a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
@@ -15,9 +15,9 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Element;
 
 /**
@@ -78,7 +78,7 @@
     delayTestFinish(5000);
 
     // Check the header IDs
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         UIObjectTest.assertDebugIdContents("myStack-text0", "header a");
         UIObjectTest.assertDebugIdContents("myStack-text1", "header b");
diff --git a/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
index dcace37..5e062b2 100644
--- a/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
 import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
@@ -25,6 +24,7 @@
 import com.google.gwt.junit.Platform;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -90,13 +90,13 @@
     p.add(bar, new Label("bar"));
 
     delayTestFinish(2000);
-    Scheduler.get().scheduleDeferred(new Command() {
+    DeferredCommand.addCommand(new Command() {
       public void execute() {
         assertEquals(128, foo.getOffsetWidth());
         assertEquals(128 - 32, foo.getOffsetHeight());
 
         p.selectTab(1);
-        Scheduler.get().scheduleDeferred(new Command() {
+        DeferredCommand.addCommand(new Command() {
           public void execute() {
             assertEquals(128, bar.getOffsetWidth());
             assertEquals(128 - 32, bar.getOffsetHeight());