Merge from 1.6 to trunk.

svn merge -r4911:4962 https://google-web-toolkit.googlecode.com/svn/releases/1.6 .
svn merge -r4963:4992 https://google-web-toolkit.googlecode.com/svn/releases/1.6 .



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4993 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index a2d8be7..08d7f85 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -33,6 +33,7 @@
 import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
 import com.google.gwt.dev.util.arg.ArgHandlerSoyc;
 import com.google.gwt.dev.util.arg.ArgHandlerWorkDirOptional;
+import com.google.gwt.util.tools.ToolBase;
 import com.google.gwt.util.tools.Utility;
 
 import java.io.File;
@@ -104,9 +105,8 @@
   }
 
   public static void main(String[] args) {
-    System.err.println("WARNING: '" + GWTCompiler.class.getName()
-        + "' is deprecated and will be removed in a future release.");
-    System.err.println("Use '" + Compiler.class.getName() + "' instead.");
+    ToolBase.legacyWarn(GWTCompiler.class, Compiler.class);
+
     /*
      * NOTE: main always exits with a call to System.exit to terminate any
      * non-daemon threads that were started in Generators. Typically, this is to
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java
index 425ca1e..1c7b3c9 100644
--- a/dev/core/src/com/google/gwt/dev/GWTShell.java
+++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -27,6 +27,7 @@
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
 import com.google.gwt.util.tools.ArgHandlerExtra;
+import com.google.gwt.util.tools.ToolBase;
 
 import java.io.File;
 
@@ -128,9 +129,8 @@
   }
 
   public static void main(String[] args) {
-    System.err.println("WARNING: '" + GWTShell.class.getName()
-        + "' is deprecated and will be removed in a future release.");
-    System.err.println("Use '" + HostedMode.class.getName() + "' instead.");
+    ToolBase.legacyWarn(GWTShell.class, HostedMode.class);
+
     /*
      * NOTE: main always exits with a call to System.exit to terminate any
      * non-daemon threads that were started in Generators. Typically, this is to
diff --git a/dev/core/src/com/google/gwt/dev/HostedModeBase.java b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
index 9f983a3..f99d8dd 100644
--- a/dev/core/src/com/google/gwt/dev/HostedModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/HostedModeBase.java
@@ -326,6 +326,27 @@
     }
   }
 
+  public static String normalizeURL(String unknownUrlText, int port, String host) {
+    if (unknownUrlText.indexOf(":") != -1) {
+      // Assume it's a full url.
+      return unknownUrlText;
+    }
+
+    // Assume it's a trailing url path.
+    if (unknownUrlText.length() > 0 && unknownUrlText.charAt(0) == '/') {
+      unknownUrlText = unknownUrlText.substring(1);
+    }
+
+    if (port != 80) {
+      // CHECKSTYLE_OFF: Not really an assembled error message, so no space
+      // after ':'.
+      return "http://" + host + ":" + port + "/" + unknownUrlText;
+      // CHECKSTYLE_ON
+    } else {
+      return "http://" + host + "/" + unknownUrlText;
+    }
+  }
+
   protected final HostedModeBaseOptions options;
 
   /**
@@ -367,26 +388,7 @@
   public abstract void launchStartupUrls(final TreeLogger logger);
 
   public final String normalizeURL(String unknownUrlText) {
-    if (unknownUrlText.indexOf(":") != -1) {
-      // Assume it's a full url.
-      return unknownUrlText;
-    }
-
-    // Assume it's a trailing url path.
-    if (unknownUrlText.length() > 0 && unknownUrlText.charAt(0) == '/') {
-      unknownUrlText = unknownUrlText.substring(1);
-    }
-
-    int port = getPort();
-    String host = getHost();
-    if (port != 80) {
-      // CHECKSTYLE_OFF: Not really an assembled error message, so no space
-      // after ':'.
-      return "http://" + host + ":" + port + "/" + unknownUrlText;
-      // CHECKSTYLE_ON
-    } else {
-      return "http://" + host + "/" + unknownUrlText;
-    }
+    return normalizeURL(unknownUrlText, getPort(), getHost());
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/RunWebApp.java b/dev/core/src/com/google/gwt/dev/RunWebApp.java
new file mode 100644
index 0000000..a2c7ad9
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/RunWebApp.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2009 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.dev;
+
+import com.google.gwt.core.ext.ServletContainer;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.HostedModeBase.OptionPort;
+import com.google.gwt.dev.HostedModeBase.OptionStartupURLs;
+import com.google.gwt.dev.shell.BrowserWidget;
+import com.google.gwt.dev.shell.jetty.JettyLauncher;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.util.tools.ArgHandlerExtra;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An experimental class for running web apps.
+ */
+public class RunWebApp {
+
+  interface RunWebAppOptions extends OptionStartupURLs, OptionPort {
+  }
+
+  static class RunWebAppOptionsImpl implements RunWebAppOptions {
+    private int port;
+    private final List<String> startupURLs = new ArrayList<String>();
+
+    public void addStartupURL(String url) {
+      startupURLs.add(url);
+    }
+
+    public int getPort() {
+      return port;
+    }
+
+    public List<String> getStartupURLs() {
+      return Collections.unmodifiableList(startupURLs);
+    }
+
+    public void setPort(int port) {
+      this.port = port;
+    }
+  }
+
+  private class ArgHandlerWar extends ArgHandlerExtra {
+    @Override
+    public boolean addExtraArg(String arg) {
+      warFile = new File(arg);
+      if (!warFile.exists()) {
+        System.err.println("Could not open war file '"
+            + warFile.getAbsolutePath() + "'");
+        return false;
+      }
+      return true;
+    }
+
+    @Override
+    public String getPurpose() {
+      return "Specifies the name(s) of the module(s)";
+    }
+
+    @Override
+    public String[] getTagArgs() {
+      return new String[] {"module[s]"};
+    }
+
+    @Override
+    public boolean isRequired() {
+      return true;
+    }
+  }
+
+  private class ArgProcessor extends ArgProcessorBase {
+    public ArgProcessor(RunWebAppOptions options) {
+      registerHandler(new HostedMode.ArgHandlerStartupURLs(options));
+      registerHandler(new HostedModeBase.ArgHandlerPort(options));
+      registerHandler(new ArgHandlerWar());
+    }
+
+    @Override
+    protected String getName() {
+      return RunWebApp.class.getName();
+    }
+  }
+
+  public static void main(String[] args) {
+    try {
+      RunWebAppOptionsImpl options = new RunWebAppOptionsImpl();
+      RunWebApp runWebApp = new RunWebApp(options);
+      ArgProcessor argProcessor = runWebApp.new ArgProcessor(options);
+      if (argProcessor.processArgs(args)) {
+        runWebApp.run();
+      }
+    } catch (Exception e) {
+      System.err.println("Unable to start Jetty server");
+      e.printStackTrace();
+    }
+  }
+
+  protected File warFile;
+
+  private final RunWebAppOptions options;
+
+  public RunWebApp(RunWebAppOptions options) {
+    this.options = options;
+  }
+
+  protected void run() {
+    PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+    logger.setMaxDetail(TreeLogger.WARN);
+    int port = options.getPort();
+    try {
+      ServletContainer scl = new JettyLauncher().start(logger, port, warFile);
+      port = scl.getPort();
+    } catch (Exception e) {
+      System.err.println("Unable to start Jetty server");
+      e.printStackTrace();
+      return;
+    }
+    if (options.getStartupURLs().isEmpty()) {
+      options.addStartupURL("/");
+    }
+    for (String startupUrl : options.getStartupURLs()) {
+      startupUrl = HostedModeBase.normalizeURL(startupUrl, port, "localhost");
+      BrowserWidget.launchExternalBrowser(logger, startupUrl);
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/ServletValidator.java b/dev/core/src/com/google/gwt/dev/ServletValidator.java
index e5605be..24f021ef 100644
--- a/dev/core/src/com/google/gwt/dev/ServletValidator.java
+++ b/dev/core/src/com/google/gwt/dev/ServletValidator.java
@@ -16,12 +16,14 @@
 package com.google.gwt.dev;
 
 import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.shell.jetty.JettyNullLogger;
 
 import org.mortbay.jetty.servlet.ServletHandler;
 import org.mortbay.jetty.servlet.ServletHolder;
 import org.mortbay.jetty.servlet.ServletMapping;
 import org.mortbay.jetty.webapp.WebAppContext;
 import org.mortbay.jetty.webapp.WebXmlConfiguration;
+import org.mortbay.log.Log;
 
 import java.io.File;
 import java.net.MalformedURLException;
@@ -38,6 +40,12 @@
  */
 class ServletValidator {
 
+  static {
+    // Suppress spammy Jetty log initialization.
+    System.setProperty("org.mortbay.log.class", JettyNullLogger.class.getName());
+    Log.getLog();
+  }
+
   public static ServletValidator create(TreeLogger logger, File webXml) {
     try {
       return create(logger, webXml.toURI().toURL());
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
index a34680a..502ce0a 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
@@ -179,10 +179,16 @@
               errors.put(jsniRefString, refErrors);
             }
           } else if (!jsniRef.className().equals("null")) {
-            GWTProblem.recordInCud(ProblemSeverities.Warning, meth, cud,
-                "Referencing class '" + jsniRef.className()
-                    + ": unable to resolve class, expect subsequent failures",
-                null);
+            /*
+             * TODO(scottb): re-enable this when we no longer get a bunch of
+             * false failures. Currently we can't resolve top level types (like
+             * boolean_Array_Rank_1_FieldSerializer), and we also don't resolve
+             * array and primitive refs, like @Z[]::class.
+             */
+            // GWTProblem.recordInCud(ProblemSeverities.Warning, meth, cud,
+            // "Referencing class '" + jsniRef.className()
+            // + ": unable to resolve class, expect subsequent failures",
+            // null);
           }
         }
 
@@ -282,6 +288,21 @@
       char[][] compoundName = getCompoundName(jsniRef);
       TypeBinding binding = cud.scope.getType(compoundName, compoundName.length);
 
+      /*
+       * TODO(scottb): we cannot currently resolve top-level types; here's some
+       * experimental code that will let us do this.
+       */
+      // ReferenceBinding binding = cud.scope.environment().askForType(
+      // compoundName);
+      // while (binding == null && compoundName.length > 1) {
+      // int newLen = compoundName.length - 1;
+      // char[][] next = new char[newLen][];
+      // System.arraycopy(compoundName, 0, next, 0, newLen - 1);
+      // next[newLen - 1] = CharOperation.concat(compoundName[newLen - 1],
+      // compoundName[newLen], '$');
+      // compoundName = next;
+      // binding = cud.scope.environment().askForType(compoundName);
+      // }
       if (binding instanceof ProblemReferenceBinding) {
         ProblemReferenceBinding prb = (ProblemReferenceBinding) binding;
         if (prb.problemId() == ProblemReasons.NotVisible) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java b/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
index 5a0ddcc..b1a018d 100644
--- a/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
+++ b/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
@@ -175,9 +175,7 @@
   private static final String EXPECTED_GWT_ONLOAD_VERSION = "1.6";
 
   public static void launchExternalBrowser(TreeLogger logger, String location) {
-    // check GWT_EXTERNAL_BROWSER first, it overrides everything else
-    LowLevel.init();
-    String browserCmd = LowLevel.getEnv("GWT_EXTERNAL_BROWSER");
+    String browserCmd = System.getenv("GWT_EXTERNAL_BROWSER");
     if (browserCmd != null) {
       browserCmd += " " + location;
       try {
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
index 651239c..5cd8c0b 100644
--- a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
@@ -19,9 +19,15 @@
 import com.google.gwt.core.ext.ServletContainerLauncher;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.dev.util.InstalledHelpInfo;
 
+import org.mortbay.component.AbstractLifeCycle;
+import org.mortbay.jetty.AbstractConnector;
+import org.mortbay.jetty.Request;
+import org.mortbay.jetty.RequestLog;
+import org.mortbay.jetty.Response;
 import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.RequestLogHandler;
 import org.mortbay.jetty.nio.SelectChannelConnector;
 import org.mortbay.jetty.webapp.WebAppClassLoader;
 import org.mortbay.jetty.webapp.WebAppContext;
@@ -29,13 +35,73 @@
 import org.mortbay.log.Logger;
 
 import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
 
 /**
- * A launcher for an embedded Jetty server.
+ * A {@link ServletContainerLauncher} for an embedded Jetty server.
  */
 public class JettyLauncher extends ServletContainerLauncher {
 
   /**
+   * Log jetty requests/responses to TreeLogger.
+   */
+  public static class JettyRequestLogger extends AbstractLifeCycle implements
+      RequestLog {
+
+    private final TreeLogger logger;
+
+    public JettyRequestLogger(TreeLogger logger) {
+      this.logger = logger;
+    }
+
+    /**
+     * Log an HTTP request/response to TreeLogger.
+     */
+    @SuppressWarnings("unchecked")
+    public void log(Request request, Response response) {
+      int status = response.getStatus();
+      if (status < 0) {
+        // Copied from NCSARequestLog
+        status = 404;
+      }
+      TreeLogger.Type logStatus, logHeaders;
+      if (status >= 500) {
+        logStatus = TreeLogger.ERROR;
+        logHeaders = TreeLogger.INFO;
+      } else if (status >= 400) {
+        logStatus = TreeLogger.WARN;
+        logHeaders = TreeLogger.INFO;
+      } else {
+        logStatus = TreeLogger.INFO;
+        logHeaders = TreeLogger.DEBUG;
+      }
+      String userString = request.getRemoteUser();
+      if (userString == null) {
+        userString = "";
+      } else {
+        userString += "@";
+      }
+      String bytesString = "";
+      if (response.getContentCount() > 0) {
+        bytesString = " " + response.getContentCount() + " bytes";
+      }
+      TreeLogger branch = logger.branch(logStatus, String.valueOf(status)
+          + " - " + request.getMethod() + ' ' + request.getUri() + " ("
+          + userString + request.getRemoteHost() + ')' + bytesString);
+      TreeLogger headers = branch.branch(logHeaders, "Request headers");
+      Enumeration<String> headerNames = request.getHeaderNames();
+      while (headerNames.hasMoreElements()) {
+        String hdr = headerNames.nextElement();
+        String hdrVal = request.getHeader(hdr);
+        headers.log(logHeaders, hdr + ": " + hdrVal);
+      }
+      // TODO(jat): add response headers
+    }
+  }
+
+  /**
    * An adapter for the Jetty logging system to GWT's TreeLogger. This
    * implementation class is only public to allow {@link Log} to instantiate it.
    * 
@@ -43,57 +109,26 @@
    * {@link Log}'s static initializer to prevent the initial log message from
    * going to stderr.
    */
-  public static final class JettyTreeLogger implements Logger {
-    private static Type nextBranchLevel;
-    private static TreeLogger nextLogger;
-
-    /**
-     * Returns true if the default constructor can be called.
-     */
-    public static boolean isDefaultConstructionReady() {
-      return nextLogger != null;
-    }
-
-    /**
-     * Call to set initial state for default construction; must be called again
-     * each time before a default instantiation occurs.
-     */
-    public static void setDefaultConstruction(TreeLogger logger,
-        Type branchLevel) {
-      if (logger == null || branchLevel == null) {
-        throw new NullPointerException();
-      }
-      nextLogger = logger;
-      nextBranchLevel = branchLevel;
-    }
-
-    private final Type branchLevel;
+  public static class JettyTreeLogger implements Logger {
     private final TreeLogger logger;
 
-    public JettyTreeLogger() {
-      this(nextLogger, nextBranchLevel);
-      nextLogger = null;
-      nextBranchLevel = null;
-    }
-
-    public JettyTreeLogger(TreeLogger logger, Type branchLevel) {
-      if (logger == null || branchLevel == null) {
+    public JettyTreeLogger(TreeLogger logger) {
+      if (logger == null) {
         throw new NullPointerException();
       }
-      this.branchLevel = branchLevel;
       this.logger = logger;
     }
 
     public void debug(String msg, Object arg0, Object arg1) {
-      logger.log(TreeLogger.DEBUG, format(msg, arg0, arg1));
+      logger.log(TreeLogger.SPAM, format(msg, arg0, arg1));
     }
 
     public void debug(String msg, Throwable th) {
-      logger.log(TreeLogger.DEBUG, msg, th);
+      logger.log(TreeLogger.SPAM, msg, th);
     }
 
     public Logger getLogger(String name) {
-      return new JettyTreeLogger(logger.branch(branchLevel, name), branchLevel);
+      return this;
     }
 
     public void info(String msg, Object arg0, Object arg1) {
@@ -101,7 +136,7 @@
     }
 
     public boolean isDebugEnabled() {
-      return logger.isLoggable(TreeLogger.DEBUG);
+      return logger.isLoggable(TreeLogger.SPAM);
     }
 
     public void setDebugEnabled(boolean enabled) {
@@ -133,8 +168,10 @@
     }
   }
 
-  private static class JettyServletContainer extends ServletContainer {
-
+  /**
+   * The resulting {@link ServletContainer} this is launched.
+   */
+  protected static class JettyServletContainer extends ServletContainer {
     private final int actualPort;
     private final File appRootDir;
     private final TreeLogger logger;
@@ -199,74 +236,197 @@
    * Also provides special class filtering to isolate the web app from the GWT
    * hosting environment.
    */
-  private final class WebAppContextWithReload extends WebAppContext {
+  protected final class WebAppContextWithReload extends WebAppContext {
+
     /**
-     * Ensures that only Jetty and other server classes can be loaded into the
-     * {@link WebAppClassLoader}. This forces the user to put any necessary
-     * dependencies into WEB-INF/lib.
+     * Specialized {@link WebAppClassLoader} that allows outside resources to be
+     * brought in dynamically from the system path. A warning is issued when
+     * this occurs.
      */
-    private final ClassLoader parentClassLoader = new ClassLoader(null) {
-      private final ClassLoader delegateTo = Thread.currentThread().getContextClassLoader();
+    private class WebAppClassLoaderExtension extends WebAppClassLoader {
+
+      public WebAppClassLoaderExtension() throws IOException {
+        super(bootStrapOnlyClassLoader, WebAppContextWithReload.this);
+      }
+
+      @Override
+      public URL findResource(String name) {
+        // Always check this ClassLoader first.
+        URL found = super.findResource(name);
+        if (found != null) {
+          return found;
+        }
+
+        // See if the outside world has it.
+        found = systemClassLoader.getResource(name);
+        if (found == null) {
+          return null;
+        }
+
+        // Specifically for META-INF/services/javax.xml.parsers.SAXParserFactory
+        String checkName = name;
+        if (checkName.startsWith("META-INF/services/")) {
+          checkName = checkName.substring("META-INF/services/".length());
+        }
+
+        // For system/server path, just return it quietly.
+        if (isServerPath(checkName) || isSystemPath(checkName)) {
+          return found;
+        }
+
+        // Warn, add containing URL to our own ClassLoader, and retry the call.
+        String warnMessage = "Server resource '"
+            + name
+            + "' could not be found in the web app, but was found on the system classpath";
+        if (!addContainingClassPathEntry(warnMessage, found, name)) {
+          return null;
+        }
+        return super.findResource(name);
+      }
+
+      /**
+       * Override to additionally consider the most commonly available JSP and
+       * XML implementation as system resources. (In fact, Jasper is in gwt-dev
+       * via embedded Tomcat, so we always hit this case.)
+       */
+      @Override
+      public boolean isSystemPath(String name) {
+        name = name.replace('/', '.');
+        return super.isSystemPath(name)
+            || name.startsWith("org.apache.jasper.")
+            || name.startsWith("org.apache.xerces.");
+      }
 
       @Override
       protected Class<?> findClass(String name) throws ClassNotFoundException {
-        if (webAppClassLoader != null
-            && (webAppClassLoader.isServerPath(name) || webAppClassLoader.isSystemPath(name))) {
-          return delegateTo.loadClass(name);
+        try {
+          return super.findClass(name);
+        } catch (ClassNotFoundException e) {
         }
-        throw new ClassNotFoundException();
+
+        // For system/server path, just try the outside world quietly.
+        if (isServerPath(name) || isSystemPath(name)) {
+          return systemClassLoader.loadClass(name);
+        }
+
+        // See if the outside world has a URL for it.
+        String resourceName = name.replace('.', '/') + ".class";
+        URL found = systemClassLoader.getResource(resourceName);
+        if (found == null) {
+          return null;
+        }
+
+        // Warn, add containing URL to our own ClassLoader, and retry the call.
+        String warnMessage = "Server class '"
+            + name
+            + "' could not be found in the web app, but was found on the system classpath";
+        if (!addContainingClassPathEntry(warnMessage, found, resourceName)) {
+          throw new ClassNotFoundException(name);
+        }
+        return super.findClass(name);
       }
 
+      private boolean addContainingClassPathEntry(String warnMessage,
+          URL resource, String resourceName) {
+        TreeLogger.Type logLevel = (System.getProperty(PROPERTY_NOWARN_WEBAPP_CLASSPATH) == null)
+            ? TreeLogger.WARN : TreeLogger.DEBUG;
+        TreeLogger branch = logger.branch(logLevel, warnMessage);
+        String classPathURL;
+        String foundStr = resource.toExternalForm();
+        if (resource.getProtocol().equals("file")) {
+          assert foundStr.endsWith(resourceName);
+          classPathURL = foundStr.substring(0, foundStr.length()
+              - resourceName.length());
+        } else if (resource.getProtocol().equals("jar")) {
+          assert foundStr.startsWith("jar:");
+          assert foundStr.endsWith("!/" + resourceName);
+          classPathURL = foundStr.substring(4, foundStr.length()
+              - (2 + resourceName.length()));
+        } else {
+          branch.log(TreeLogger.ERROR,
+              "Found resouce but unrecognized URL format: '" + foundStr + '\'');
+          return false;
+        }
+        branch = branch.branch(logLevel, "Adding classpath entry '"
+            + classPathURL + "' to the web app classpath for this session",
+            null, new InstalledHelpInfo("webAppClassPath.html"));
+        try {
+          addClassPath(classPathURL);
+          return true;
+        } catch (IOException e) {
+          branch.log(TreeLogger.ERROR, "Failed add container URL: '"
+              + classPathURL + '\'', e);
+          return false;
+        }
+      }
+    }
+
+    /**
+     * Parent ClassLoader for the Jetty web app, which can only load JVM
+     * classes. We would just use <code>null</code> for the parent ClassLoader
+     * except this makes Jetty unhappy.
+     */
+    private final ClassLoader bootStrapOnlyClassLoader = new ClassLoader(null) {
     };
 
-    private WebAppClassLoader webAppClassLoader;
+    private final TreeLogger logger;
+
+    /**
+     * In the usual case of launching {@link com.google.gwt.dev.HostedMode},
+     * this will always by the system app ClassLoader.
+     */
+    private final ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader();
 
     @SuppressWarnings("unchecked")
-    private WebAppContextWithReload(String webApp, String contextPath) {
+    private WebAppContextWithReload(TreeLogger logger, String webApp,
+        String contextPath) {
       super(webApp, contextPath);
+      this.logger = logger;
+
       // Prevent file locking on Windows; pick up file changes.
       getInitParams().put(
           "org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false");
+
+      // Since the parent class loader is bootstrap-only, prefer it first.
+      setParentLoaderPriority(true);
     }
 
     @Override
     protected void doStart() throws Exception {
-      webAppClassLoader = new WebAppClassLoader(parentClassLoader, this);
-      setClassLoader(webAppClassLoader);
+      setClassLoader(new WebAppClassLoaderExtension());
       super.doStart();
     }
 
     @Override
     protected void doStop() throws Exception {
       super.doStop();
-      webAppClassLoader = null;
       setClassLoader(null);
     }
   }
 
+  /**
+   * System property to suppress warnings about loading web app classes from the
+   * system classpath.
+   */
+  private static final String PROPERTY_NOWARN_WEBAPP_CLASSPATH = "gwt.nowarn.webapp.classpath";
+
+  static {
+    // Suppress spammy Jetty log initialization.
+    System.setProperty("org.mortbay.log.class", JettyNullLogger.class.getName());
+    Log.getLog();
+  }
+
   public ServletContainer start(TreeLogger logger, int port, File appRootDir)
       throws Exception {
     checkStartParams(logger, port, appRootDir);
 
-    // The dance we do with Jetty's logging system -- disabled, log to console.
-    if (false) {
-      System.setProperty("VERBOSE", "true");
-      JettyTreeLogger.setDefaultConstruction(logger, TreeLogger.INFO);
-      System.setProperty("org.mortbay.log.class",
-          JettyTreeLogger.class.getName());
-      // Force initialization.
-      Log.isDebugEnabled();
-      if (JettyTreeLogger.isDefaultConstructionReady()) {
-        // The log system was already initialized and did not use our
-        // newly-constructed logger, set it explicitly now.
-        Log.setLog(new JettyTreeLogger());
-      }
-    }
+    // Setup our own logger.
+    Log.setLog(new JettyTreeLogger(logger));
 
     // Turn off XML validation.
     System.setProperty("org.mortbay.xml.XmlParser.Validating", "false");
 
-    SelectChannelConnector connector = new SelectChannelConnector();
+    AbstractConnector connector = getConnector();
     connector.setPort(port);
 
     // Don't share ports with an existing process.
@@ -279,10 +439,13 @@
     server.addConnector(connector);
 
     // Create a new web app in the war directory.
-    WebAppContext wac = new WebAppContextWithReload(
+    WebAppContext wac = new WebAppContextWithReload(logger,
         appRootDir.getAbsolutePath(), "/");
 
-    server.setHandler(wac);
+    RequestLogHandler logHandler = new RequestLogHandler();
+    logHandler.setRequestLog(new JettyRequestLogger(logger));
+    logHandler.setHandler(wac);
+    server.setHandler(logHandler);
     server.start();
     server.setStopAtShutdown(true);
 
@@ -290,6 +453,10 @@
         connector.getLocalPort(), appRootDir);
   }
 
+  protected AbstractConnector getConnector() {
+    return new SelectChannelConnector();
+  }
+
   private void checkStartParams(TreeLogger logger, int port, File appRootDir) {
     if (logger == null) {
       throw new NullPointerException("logger cannot be null");
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyNullLogger.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyNullLogger.java
new file mode 100644
index 0000000..81418ad
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyNullLogger.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2009 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.dev.shell.jetty;
+
+import org.mortbay.log.Logger;
+
+/**
+ * A Jetty {@link Logger} that suppresses all output.
+ */
+public class JettyNullLogger implements Logger {
+
+  public void debug(String msg, Throwable th) {
+  }
+
+  public void debug(String msg, Object arg0, Object arg1) {
+  }
+
+  public Logger getLogger(String name) {
+    return this;
+  }
+
+  public void info(String msg, Object arg0, Object arg1) {
+  }
+
+  public boolean isDebugEnabled() {
+    return false;
+  }
+
+  public void setDebugEnabled(boolean enabled) {
+  }
+
+  public void warn(String msg, Throwable th) {
+  }
+
+  public void warn(String msg, Object arg0, Object arg1) {
+  }
+}
diff --git a/dev/core/src/com/google/gwt/util/tools/ToolBase.java b/dev/core/src/com/google/gwt/util/tools/ToolBase.java
index b965bd3..5e0cc0b 100644
--- a/dev/core/src/com/google/gwt/util/tools/ToolBase.java
+++ b/dev/core/src/com/google/gwt/util/tools/ToolBase.java
@@ -48,6 +48,8 @@
  */
 public abstract class ToolBase {
 
+  private static final String PROPERTY_NOWARN_LEGACY_TOOLS = "gwt.nowarn.legacy.tools";
+
   static {
     String installPath = Utility.getInstallPath();
     try {
@@ -59,6 +61,16 @@
     System.setProperty("swt.library.path", installPath + '/');
   }
 
+  public static void legacyWarn(Class<?> legacy, Class<?> replacement) {
+    if (System.getProperty(PROPERTY_NOWARN_LEGACY_TOOLS) == null) {
+      System.err.println("WARNING: '" + legacy.getName()
+          + "' is deprecated and will be removed in a future release.");
+      System.err.println("Use '" + replacement.getName() + "' instead.");
+      System.err.println("(To disable this warning, pass -D"
+          + PROPERTY_NOWARN_LEGACY_TOOLS + " as a JVM arg.)");
+    }
+  }
+
   /**
    * Use a linked hash map to preserve the declaration order.
    */
diff --git a/distro-source/core/src/doc/helpInfo/webAppClassPath.html b/distro-source/core/src/doc/helpInfo/webAppClassPath.html
new file mode 100644
index 0000000..8932f64
--- /dev/null
+++ b/distro-source/core/src/doc/helpInfo/webAppClassPath.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Web App Classpath Problem</title>
+</head>
+<body>
+
+<h1>Web App Classpath Problem</h1>
+
+<p>You were directed to this help topic because your server code needed a class or
+resource that was not found on the <i>web app classpath</i>, but <b>was</b> found
+on the <i>system classpath</i>. The <i>system classpath</i> is the classpath
+you specify when launching the Java VM to run hosted mode.  The <i>web app
+classpath</i> is different &mdash; it consists of classes that live in your web
+application's <i>war directory</i>.  All server classes and dependencies should
+to be placed in your war directory: libraries (jars) should be placed in
+<nobr><code>war/WEB-INF/lib/</code></nobr> and classes that don't live in 
+jars should be placed in <nobr><code>war/WEB-INF/classes/</code></nobr>.
+
+<p>GWT hosted mode helpfully works around this problem by mapping these outside
+resources into your web app classpath.  This warning reminds you that failing to
+address the issue can lead to problems when you actually deploy your web app to
+a real server.</p>
+
+<h2>Tips</h2>
+
+<ul>
+<li>The most common reason to encounter this problem with a new project is
+using RPC, which tries to load 
+<nobr><code>com.google.gwt.user.client.rpc.RemoteService</code></nobr>. The
+solution to is copy <code>gwt-servlet.jar</code> from the GWT install directory
+into your web app's <nobr><code>war/WEB-INF/lib/</code></nobr> directory.</li>
+<li>If you have a good reason for not following the recommended configuration,
+you can suppress warning by setting the Java system property
+<nobr><code>gwt.nowarn.webapp.classpath</code></nobr>.  Specify
+<nobr><code>-Dgwt.nowarn.webapp.classpath</code></nobr> as a JVM argument when 
+launching hosted mode.
+</li>
+</ul>
diff --git a/distro-source/linux/build.xml b/distro-source/linux/build.xml
index 5fde893..201c2dd 100755
--- a/distro-source/linux/build.xml
+++ b/distro-source/linux/build.xml
@@ -12,7 +12,7 @@
       <tarfileset file="${gwt.build.lib}/gwt-dev-oophm.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-user.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-servlet.jar" prefix="${project.distname}" />
-      <tarfileset file="${gwt.build.lib}/gwt-benchmark-viewer.jar" prefix="${project.distname}" />
+      <tarfileset file="${gwt.build.lib}/gwt-benchmark-viewer.war" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-soyc-vis.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-api-checker.jar" prefix="${project.distname}" />
 
diff --git a/distro-source/linux/src/benchmarkViewer b/distro-source/linux/src/benchmarkViewer
index 7b308d7..d7759b4 100755
--- a/distro-source/linux/src/benchmarkViewer
+++ b/distro-source/linux/src/benchmarkViewer
@@ -1,3 +1,3 @@
 #!/bin/sh
 APPDIR=`dirname $0`;
-java -Dcom.google.gwt.junit.reportPath="$1" -cp "$APPDIR/gwt-user.jar:$APPDIR/gwt-dev-linux.jar:$APPDIR/gwt-benchmark-viewer.jar" com.google.gwt.dev.GWTShell -port auto com.google.gwt.benchmarks.viewer.ReportViewer/ReportViewer.html?gwt.hybrid;
+java -Dcom.google.gwt.junit.reportPath="$1" -cp "$APPDIR/gwt-dev-linux.jar" com.google.gwt.dev.RunWebApp -port auto $APPDIR/gwt-benchmark-viewer.war;
diff --git a/distro-source/mac/build.xml b/distro-source/mac/build.xml
index 4114eb6..8737abc 100755
--- a/distro-source/mac/build.xml
+++ b/distro-source/mac/build.xml
@@ -12,7 +12,7 @@
       <tarfileset file="${gwt.build.lib}/gwt-dev-oophm.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-user.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-servlet.jar" prefix="${project.distname}" />
-      <tarfileset file="${gwt.build.lib}/gwt-benchmark-viewer.jar" prefix="${project.distname}" />
+      <tarfileset file="${gwt.build.lib}/gwt-benchmark-viewer.war" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-soyc-vis.jar" prefix="${project.distname}" />
       <tarfileset file="${gwt.build.lib}/gwt-api-checker.jar" prefix="${project.distname}" />
 
diff --git a/distro-source/mac/src/benchmarkViewer b/distro-source/mac/src/benchmarkViewer
index acf4b22..097bd1c 100755
--- a/distro-source/mac/src/benchmarkViewer
+++ b/distro-source/mac/src/benchmarkViewer
@@ -1,3 +1,3 @@
 #!/bin/sh
 APPDIR=`dirname $0`;
-java  -Dcom.google.gwt.junit.reportPath="$1" -XstartOnFirstThread -cp "$APPDIR/gwt-user.jar:$APPDIR/gwt-dev-mac.jar:$APPDIR/gwt-benchmark-viewer.jar" com.google.gwt.dev.GWTShell -port auto com.google.gwt.benchmarks.viewer.ReportViewer/ReportViewer.html?gwt.hybrid;
+java  -Dcom.google.gwt.junit.reportPath="$1" -XstartOnFirstThread -cp "$APPDIR/gwt-dev-mac.jar" com.google.gwt.dev.RunWebApp -port auto $APPDIR/gwt-benchmark-viewer.war;
diff --git a/distro-source/windows/build.xml b/distro-source/windows/build.xml
index 6110f07..0b858a6 100755
--- a/distro-source/windows/build.xml
+++ b/distro-source/windows/build.xml
@@ -12,7 +12,7 @@
       <zipfileset file="${gwt.build.lib}/gwt-dev-oophm.jar" prefix="${project.distname}" />
       <zipfileset file="${gwt.build.lib}/gwt-user.jar" prefix="${project.distname}" />
       <zipfileset file="${gwt.build.lib}/gwt-servlet.jar" prefix="${project.distname}" />
-      <zipfileset file="${gwt.build.lib}/gwt-benchmark-viewer.jar" prefix="${project.distname}" />
+      <zipfileset file="${gwt.build.lib}/gwt-benchmark-viewer.war" prefix="${project.distname}" />
       <zipfileset file="${gwt.build.lib}/gwt-soyc-vis.jar" prefix="${project.distname}" />
       <zipfileset file="${gwt.build.lib}/gwt-api-checker.jar" prefix="${project.distname}" />
 
diff --git a/distro-source/windows/src/benchmarkViewer.cmd b/distro-source/windows/src/benchmarkViewer.cmd
index bddc9d9..7bf3ee2 100755
--- a/distro-source/windows/src/benchmarkViewer.cmd
+++ b/distro-source/windows/src/benchmarkViewer.cmd
@@ -1 +1 @@
-@java -Dcom.google.gwt.junit.reportPath="%1" -cp "%~dp0/gwt-user.jar;%~dp0/gwt-dev-windows.jar;%~dp0/gwt-benchmark-viewer.jar" com.google.gwt.dev.GWTShell -port auto com.google.gwt.benchmarks.viewer.ReportViewer/ReportViewer.html?gwt.hybrid;
+@java -Dcom.google.gwt.junit.reportPath="%1" -cp "%~dp0/gwt-dev-windows.jar" com.google.gwt.dev.RunWebApp -port auto %~dp0/gwt-benchmark-viewer.war
\ No newline at end of file
diff --git a/doc/build.xml b/doc/build.xml
index 5bcc340..eaa0334 100644
--- a/doc/build.xml
+++ b/doc/build.xml
@@ -10,7 +10,7 @@
   <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-linux.jar" />
 
   <property name="USER_PKGS"
-          value="com.google.gwt.animation.client;com.google.gwt.benchmarks.client;com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.soyc;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.debug.client;com.google.gwt.dom.client;com.google.gwt.event.dom.client;com.google.gwt.event.logical.shared;com.google.gwt.event.shared;com.google.gwt.http.client;com.google.gwt.i18n.client;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.server.rpc;com.google.gwt.xml.client;com.google.gwt.user.datepicker.client"/>
+          value="com.google.gwt.animation.client;com.google.gwt.benchmarks.client;com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.soyc;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.debug.client;com.google.gwt.dom.client;com.google.gwt.event.dom.client;com.google.gwt.event.logical.shared;com.google.gwt.event.shared;com.google.gwt.http.client;com.google.gwt.i18n.client;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.datepicker.client;com.google.gwt.user.server.rpc;com.google.gwt.xml.client"/>
   <property name="LANG_PKGS" value="java.lang;java.lang.annotation;java.util;java.io;java.sql" />
 
   <!--	Individual classes to include when we don't want to 
diff --git a/eclipse/tools/benchmark-viewer/.checkstyle b/eclipse/tools/benchmark-viewer/.checkstyle
new file mode 100644
index 0000000..1e5b59a
--- /dev/null
+++ b/eclipse/tools/benchmark-viewer/.checkstyle
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<fileset-config file-format-version="1.2.0" simple-config="true">

+    <fileset name="all" enabled="true" check-config-name="GWT Checks" local="false">

+        <file-match-pattern match-pattern="." include-pattern="true"/>

+    </fileset>

+    <filter name="NonSrcDirs" enabled="true"/>

+</fileset-config>

diff --git a/eclipse/tools/benchmark-viewer/.project b/eclipse/tools/benchmark-viewer/.project
index 5e12fb6..65867f4 100644
--- a/eclipse/tools/benchmark-viewer/.project
+++ b/eclipse/tools/benchmark-viewer/.project
@@ -1,24 +1,30 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>benchmark-viewer</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-	<linkedResources>
-		<link>
-			<name>src</name>
-			<type>2</type>
-			<locationURI>GWT_ROOT/tools/benchmark-viewer/src</locationURI>
-		</link>
-	</linkedResources>
-</projectDescription>
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>benchmark-viewer</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+		<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>

+	</natures>

+	<linkedResources>

+		<link>

+			<name>core</name>

+			<type>2</type>

+			<locationURI>GWT_ROOT/tools/benchmark-viewer</locationURI>

+		</link>

+	</linkedResources>

+</projectDescription>

diff --git a/tools/api-checker/config/gwt15_16userApi.conf b/tools/api-checker/config/gwt15_16userApi.conf
index 1df4179c..9a9e28d 100644
--- a/tools/api-checker/config/gwt15_16userApi.conf
+++ b/tools/api-checker/config/gwt15_16userApi.conf
@@ -106,3 +106,7 @@
 com.google.gwt.i18n.client.LocaleInfo::getLocaleName() FINAL_ADDED
 com.google.gwt.i18n.client.LocaleInfo::isRTL() FINAL_ADDED
 
+# The following 2 nested classes should never have been public to begin with.
+com.google.gwt.user.client.ui.UIObject.DebugIdImpl MISSING
+com.google.gwt.user.client.ui.UIObject.DebugIdImplEnabled MISSING
+
diff --git a/tools/benchmark-viewer/build.xml b/tools/benchmark-viewer/build.xml
index 398350f..d9d4481 100755
--- a/tools/benchmark-viewer/build.xml
+++ b/tools/benchmark-viewer/build.xml
@@ -1,80 +1,84 @@
 <project name="benchmark-viewer" default="build" basedir=".">
 
-        <!--
-          TODO(tobyr) 
+  <!--
+    TODO(tobyr) 
 
-          Once we have more than a single tool, this build should be re-examined
-          to see if several of the targets should be lifted into common.ant.xml.
-          The simple targets, like tests*, clean, and checkstyle are good
-          candidates, while the other targets depend heavily on the actual
-          makeup of the future tools.
-        -->
+    Once we have more than a single tool, this build should be re-examined
+    to see if several of the targets should be lifted into common.ant.xml.
+    The simple targets, like tests*, clean, and checkstyle are good
+    candidates, while the other targets depend heavily on the actual
+    makeup of the future tools.
+  -->
 
-        <property name="gwt.root" location="../.." />
+  <property name="gwt.root" location="../.." />
   <property name="project.tail" value="tools/benchmark-viewer" />
   <import file="${gwt.root}/common.ant.xml" />
 
-        <property name="tools.build" value="${gwt.build.out}/${project.tail}" />
-        <property name="tools.module" value="com.google.gwt.benchmarks.viewer.ReportViewer" />
-        <property name="tools.module.path" value="com/google/gwt/benchmarks/viewer/public" />
-
-  <!--
-    Default hosted mode test cases
-  -->
-  <fileset id="default.hosted.tests" dir="${javac.junit.out}">
-    <include name="**/*Test.class" />
-  </fileset>
-
-  <!--
-    Default web mode test cases
-  -->
-  <fileset id="default.web.tests" dir="${javac.junit.out}">
-    <include name="**/*Test.class" />
-  </fileset>
-
-  <!-- Platform shouldn't matter here, just picking one -->
+    <!-- Platform shouldn't matter here, just picking one -->
   <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-linux.jar" />
   <property.ensure name="gwt.user.jar" location="${gwt.build.lib}/gwt-user.jar" />
+  <property.ensure name="gwt.servlet.jar" location="${gwt.build.lib}/gwt-servlet.jar" />
 
-  <target name="compile" description="Compile all class files">
-    <mkdir dir="${javac.out}" />
-    <gwt.javac>
-      <classpath>
-        <pathelement location="${gwt.tools.lib}/tomcat/servlet-api-2.4.jar" />
-        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
-        <pathelement location="${gwt.tools.lib}/jfreechart/jfreechart-1.0.3.jar" />
-        <pathelement location="${gwt.tools.lib}/jfreechart/jcommon-1.0.6.jar" />
-        <pathelement location="${gwt.dev.jar}" />
-        <pathelement location="${gwt.user.jar}" />
-      </classpath>
-    </gwt.javac>
+  <property name="war" location="${project.build}/war" />
+
+  <path id="project.class.path">
+    <pathelement location="war/WEB-INF/classes"/>
+    <pathelement location="${gwt.user.jar}"/>
+    <pathelement location="${gwt.dev.jar}"/>
+    <fileset dir="${war}/WEB-INF/lib" includes="**/*.jar"/>
+  </path>
+
+  <target name="wardir" description="Create the target war directory">
+    <copy todir="${war}">
+      <fileset dir="war" excludes="WEB-INF/classes/marker" />
+    </copy>
+    <mkdir dir="${war}/WEB-INF/lib" />
+    <copy todir="${war}/WEB-INF/lib" file="${gwt.servlet.jar}" />
+    <copy todir="${war}/WEB-INF/lib" file="${gwt.tools.lib}/jfreechart/jfreechart-1.0.3.jar" />
+    <copy todir="${war}/WEB-INF/lib" file="${gwt.tools.lib}/jfreechart/jcommon-1.0.6.jar" />
   </target>
 
-  <target name="compile.tests" description="Compiles the test code for this project">
-    <mkdir dir="${javac.junit.out}" />
-    <gwt.javac srcdir="test" destdir="${javac.junit.out}">
-      <classpath>
-        <pathelement location="${javac.out}" />
-        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
-        <pathelement location="${gwt.dev.staging.jar}" />
-      </classpath>
+  <target name="javac" depends="wardir" description="Compile java source">
+    <mkdir dir="${war}/WEB-INF/classes"/>
+    <gwt.javac destdir="${war}/WEB-INF/classes">
+      <classpath refid="project.class.path"/>
     </gwt.javac>
+    <copy todir="${war}/WEB-INF/classes">
+      <fileset dir="src" excludes="**/*.java"/>
+    </copy>
   </target>
 
-  <target name="build" depends="compile, gwtc" description="Build and package this project">
-    <mkdir dir="${gwt.build.lib}" />
-                <copy todir="${tools.build}/www-new/${tools.module.path}" flatten="true" >
-                  <fileset dir="${tools.build}/www/${tools.module}" />
-                </copy>
-                    
-    <gwt.jar>
-      <fileset dir="src" excludes="**/package.html" />
-      <fileset dir="${javac.out}" />
-                        <fileset dir="${tools.build}/www-new" />
-                        <zipfileset src="${gwt.tools.lib}/jfreechart/itext-1.4.6.jar"/>
-                        <zipfileset src="${gwt.tools.lib}/jfreechart/jcommon-1.0.6.jar"/>
-                        <zipfileset src="${gwt.tools.lib}/jfreechart/jfreechart-1.0.3.jar"/>
-    </gwt.jar>
+  <target name="gwtc" depends="javac" description="GWT compile to JavaScript">
+    <outofdate>
+      <sourcefiles>
+        <fileset dir="src"/>
+        <path refid="project.class.path"/>
+      </sourcefiles>
+      <targetfiles path="${war}/reportViewer/reportViewer.nocache.js" />
+      <sequential>
+        <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
+          <classpath>
+            <pathelement location="src"/>
+            <path refid="project.class.path"/>
+          </classpath>
+          <jvmarg value="-Xmx256M"/>
+          <arg value="-war"/>
+          <arg value="${war}"/>
+          <arg value="com.google.gwt.benchmarks.viewer.ReportViewer"/>
+        </java>
+      </sequential>
+    </outofdate>
+  </target>
+
+  <target name="war" depends="gwtc" description="Create a war file">
+    <zip destfile="${gwt.build.lib}/gwt-benchmark-viewer.war" basedir="${war}"/>
+  </target>
+
+  <target name="build" depends="war" description="Build this project" />
+
+  <target name="clean" description="Cleans this project">
+    <delete dir="${war}" failonerror="false" />
+    <delete file="${gwt.build.lib}/gwt-benchmark-viewer.war" failonerror="false" />
   </target>
 
   <target name="checkstyle" description="Static analysis of source">
@@ -83,55 +87,5 @@
     </gwt.checkstyle>
   </target>
 
-  <target name="gwtc" description="Compile to JavaScript">
-      <mkdir dir="${tools.build}/www" />
-    <outofdate>
-      <sourcefiles>
-        <fileset dir="src" />
-        <fileset file="${gwt.user.jar}" />
-        <fileset file="${gwt.dev.jar}" />
-      </sourcefiles>
-      <targetfiles>
-        <fileset dir="${tools.build}/www" />
-      </targetfiles>
-      <sequential>
-        <java dir="${tools.build}" classname="com.google.gwt.dev.GWTCompiler" classpath="src:${gwt.user.jar}:${gwt.dev.jar}" fork="yes" failonerror="true">
-          <jvmarg value="-Xmx256M"/>
-          <arg value="-out" />
-          <arg file="${tools.build}/www" />
-          <arg value="${tools.module}" />
-        </java>
-      </sequential>
-    </outofdate>
-  </target>
-
-  <target name="remoteweb-test" description="Run a remoteweb test at the given host and path">
-    <echo message="Performing remote browser testing at rmi://${gwt.remote.browser}" />
-    <gwt.junit test.args="-out www -web -remoteweb rmi://${gwt.remote.browser}" test.out="${junit.out}/${gwt.remote.browser}" test.cases="default.web.tests" />
-  </target>
-
-  <target name="test" depends="compile, compile.tests" description="Run hosted-mode, web-mode and remoteweb tests for this project.">
-    <property.ensure name="distro.built" location="${gwt.dev.staging.jar}" message="GWT must be built before performing any tests.  This can be fixed by running ant in the ${gwt.root} directory." />
-
-    <!--
-      Run hosted and web mode tests for the platform on which this build
-      is executing
-    -->
-    <parallel threadcount="1">
-      <gwt.junit test.out="${junit.out}/${build.host.platform}-hosted-mode" test.cases="default.hosted.tests" />
-
-      <gwt.junit test.args="-out www -web" test.out="${junit.out}/${build.host.platform}-web-mode" test.cases="default.web.tests" />
-
-      <!--
-        Run remote browser testing for the comma delimited list of remote browsers
-      -->
-      <foreach list="${gwt.remote.browsers}" delimiter="," parallel="true" maxThreads="1" target="remoteweb-test" param="gwt.remote.browser" />
-    </parallel>
-  </target>
-
-  <target name="clean" description="Cleans this project's intermediate and output files">
-    <delete dir="${project.build}" />
-    <delete file="${project.lib}" />
-  </target>
+  <target name="test" depends="build" />
 </project>
-
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/ReportViewer.gwt.xml b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/ReportViewer.gwt.xml
index 96ab3a2..0dd5d3e 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/ReportViewer.gwt.xml
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/ReportViewer.gwt.xml
@@ -14,7 +14,7 @@
 
 <!-- Deferred binding rules for browser selection.                          -->
 <!--                                                                        -->
-<module>
+<module rename-to="reportViewer">
   <inherits name="com.google.gwt.user.User"/>
   <inherits name="com.google.gwt.http.HTTP"/>
 
@@ -23,7 +23,5 @@
   <entry-point class="com.google.gwt.benchmarks.viewer.client.ReportViewer"/>
 
   <servlet path='/test_reports' class='com.google.gwt.benchmarks.viewer.server.ReportServerImpl'/>
-  <servlet path='/test_images' class='com.google.gwt.benchmarks.viewer.server.ReportImageServer'/>
-
-  <stylesheet src="ReportViewer.css"/>
+  <servlet path='/test_images/*' class='com.google.gwt.benchmarks.viewer.server.ReportImageServer'/>
 </module>
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServer.java b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServer.java
index 74f554b..07810ed 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServer.java
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServer.java
@@ -16,6 +16,7 @@
 package com.google.gwt.benchmarks.viewer.client;
 
 import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
 import java.util.List;
 
@@ -26,6 +27,7 @@
  * @see com.google.gwt.junit.viewer.server.ReportServerImpl
  * @see ReportViewer
  */
+@RemoteServiceRelativePath("test_reports")
 public interface ReportServer extends RemoteService {
 
   /**
@@ -34,12 +36,12 @@
    * @param reportId The id of the report. Originates from the ReportSummary.
    * @return the matching Report, or null if the Report could not be found.
    */
-  public Report getReport(String reportId);
+  Report getReport(String reportId);
 
   /**
    * Returns a list of summaries of all the Benchmark reports.
    * 
    * @return a non-null list of ReportSummary
    */
-  public List<ReportSummary> getReportSummaries();
+  List<ReportSummary> getReportSummaries();
 }
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServerAsync.java b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServerAsync.java
index 03d7bac..d128334 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServerAsync.java
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportServerAsync.java
@@ -26,7 +26,7 @@
  */
 public interface ReportServerAsync {
 
-  public void getReport(String reportId, AsyncCallback<Report> callback);
+  void getReport(String reportId, AsyncCallback<Report> callback);
 
-  public void getReportSummaries(AsyncCallback<List<ReportSummary>> callback);
+  void getReportSummaries(AsyncCallback<List<ReportSummary>> callback);
 }
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportViewer.java b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportViewer.java
index 3132754..d4e2bb3 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportViewer.java
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/client/ReportViewer.java
@@ -23,7 +23,6 @@
 import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.HistoryListener;
 import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.ServiceDefTarget;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.CellPanel;
 import com.google.gwt.user.client.ui.ClickListener;
@@ -36,10 +35,10 @@
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SourcesTableEvents;
+import com.google.gwt.user.client.ui.TableListener;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.TableListener;
-import com.google.gwt.user.client.ui.SourcesTableEvents;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -65,6 +64,7 @@
  * from by setting the system property named in
  * {@link com.google.gwt.benchmarks.client.Benchmark#REPORT_PATH}.
  */
+@SuppressWarnings("deprecation")
 public class ReportViewer implements EntryPoint, HistoryListener {
 
   private static class MutableBool {
@@ -79,17 +79,18 @@
   private class SummariesTableListener implements TableListener {
 
     public void onCellClicked(SourcesTableEvents sender, int row, int col) {
-      ReportSummary summary = summaries.get(row - 1);
-      String token = summary.getId();
-      // Short circuit the history loop.
-      selectReport(row, token);
-      History.newItem(token);
+      if (row > 0) {
+        ReportSummary summary = summaries.get(row - 1);
+        String token = summary.getId();
+        // Short circuit the history loop.
+        selectReport(row, token);
+        History.newItem(token);
+      }
     }
   }
 
-  private static final String baseUrl = GWT.getModuleBaseURL();
-
-  private static final String imageServer = baseUrl + "test_images/";
+  private static final String imageServer = GWT.getModuleBaseURL()
+      + "test_images/";
 
   HTML detailsLabel;
 
@@ -132,9 +133,7 @@
     init();
 
     // Asynchronously load the summaries
-    ServiceDefTarget target = (ServiceDefTarget) GWT.create(ReportServer.class);
-    target.setServiceEntryPoint(GWT.getModuleBaseURL() + "test_reports");
-    reportServer = (ReportServerAsync) target;
+    reportServer = (ReportServerAsync) GWT.create(ReportServer.class);
 
     reportServer.getReportSummaries(new AsyncCallback<List<ReportSummary>>() {
       public void onFailure(Throwable caught) {
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/ReportViewer.html b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/ReportViewer.html
deleted file mode 100644
index 7842c32..0000000
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/ReportViewer.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<html>
-	<head>
-		<title>ReportViewer</title>
-	</head>
-	<body>
-		<script language="javascript" src="com.google.gwt.benchmarks.viewer.ReportViewer.nocache.js"></script>
-		<iframe src="javascript:''" id='__gwt_historyFrame' style='width:0;height:0;border:0'></iframe>
-	</body>
-</html>
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportImageServer.java b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportImageServer.java
index 07ca35d..3fbd66c 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportImageServer.java
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportImageServer.java
@@ -35,7 +35,6 @@
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
 import org.jfree.data.category.DefaultCategoryDataset;
-import org.jfree.data.xy.XYDataItem;
 import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;
 
@@ -96,7 +95,8 @@
     try {
       handleRequest(request, response);
     } catch (Exception e) {
-      if (e.getClass().getName().endsWith("ClientAbortException")) {
+      if (e.getClass().getName().endsWith(".ClientAbortException")
+          || e.getClass().getName().endsWith(".EofException")) {
         // No big deal, the client browser terminated a download.
       } else {
         logException("An error occured while trying to create the chart.", e,
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/ReportViewer.css b/tools/benchmark-viewer/war/ReportViewer.css
similarity index 100%
rename from tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/ReportViewer.css
rename to tools/benchmark-viewer/war/ReportViewer.css
diff --git a/tools/benchmark-viewer/war/ReportViewer.html b/tools/benchmark-viewer/war/ReportViewer.html
new file mode 100644
index 0000000..8c6f35a
--- /dev/null
+++ b/tools/benchmark-viewer/war/ReportViewer.html
@@ -0,0 +1,10 @@
+<html>
+	<head>
+    <link type="text/css" rel="stylesheet" href="ReportViewer.css">
+		<title>ReportViewer</title>
+	</head>
+	<body>
+		<script language="javascript" src="reportViewer/reportViewer.nocache.js"></script>
+		<iframe src="javascript:''" id='__gwt_historyFrame' style='width:0;height:0;border:0'></iframe>
+	</body>
+</html>
diff --git a/tools/benchmark-viewer/war/WEB-INF/classes/marker b/tools/benchmark-viewer/war/WEB-INF/classes/marker
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/benchmark-viewer/war/WEB-INF/classes/marker
diff --git a/tools/benchmark-viewer/war/WEB-INF/web.xml b/tools/benchmark-viewer/war/WEB-INF/web.xml
new file mode 100644
index 0000000..301ee6c
--- /dev/null
+++ b/tools/benchmark-viewer/war/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app>
+
+  <!-- Default page to serve -->
+  <welcome-file-list>
+    <welcome-file>ReportViewer.html</welcome-file>
+  </welcome-file-list>
+  
+  <!-- Servlets -->
+  <servlet>
+    <servlet-name>test_reports</servlet-name>
+    <servlet-class>com.google.gwt.benchmarks.viewer.server.ReportServerImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>test_reports</servlet-name>
+    <url-pattern>/reportViewer/test_reports</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>test_images</servlet-name>
+    <servlet-class>com.google.gwt.benchmarks.viewer.server.ReportImageServer</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>test_images</servlet-name>
+    <url-pattern>/reportViewer/test_images/*</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/gradient.gif b/tools/benchmark-viewer/war/gradient.gif
similarity index 100%
rename from tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/public/gradient.gif
rename to tools/benchmark-viewer/war/gradient.gif
Binary files differ
diff --git a/user/src/com/google/gwt/debug/client/DebugInfo.java b/user/src/com/google/gwt/debug/client/DebugInfo.java
index c7405b4..94474b6 100644
--- a/user/src/com/google/gwt/debug/client/DebugInfo.java
+++ b/user/src/com/google/gwt/debug/client/DebugInfo.java
@@ -18,13 +18,15 @@
 import com.google.gwt.core.client.GWT;
 
 /**
- * This class provides a set of static methods related to Debugging.
+ * Provides low-level functionality to support the creation of testing and diagnostic frameworks.
+ * 
+ * @see com.google.gwt.user.client.ui.UIObject#ensureDebugId(String)
  */
 public class DebugInfo {
   /**
    * Implementation class for {@link DebugInfo}.
    */
-  public static class DebugInfoImpl {
+  private static class DebugInfoImpl {
     public boolean isDebugIdEnabled() {
       return false;
     }
@@ -33,7 +35,8 @@
   /**
    * Implementation class for {@link DebugInfo} used when debug IDs are enabled.
    */
-  public static class DebugInfoImplEnabled extends DebugInfoImpl {
+  @SuppressWarnings("unused")
+  private static class DebugInfoImplEnabled extends DebugInfoImpl {
     @Override
     public boolean isDebugIdEnabled() {
       return true;
diff --git a/user/src/com/google/gwt/debug/client/package-info.java b/user/src/com/google/gwt/debug/client/package-info.java
new file mode 100644
index 0000000..896e007
--- /dev/null
+++ b/user/src/com/google/gwt/debug/client/package-info.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * Provides low-level functionality to support the creation of testing and
+ * diagnostic frameworks.
+ * 
+ * <p>
+ * To use the functionality in this package, your GWT module should inherit the
+ * module <code>com.google.gwt.debug.Debug</code>. The <code>Debug</code>
+ * module introduces the client property <code>gwt.enableDebugId</code>,
+ * which controls whether or not this debug code is enabled (and therefore
+ * included in the final compiled result). It is set to <code>true</code> by
+ * default, but a module being compiled for production would very likely want to
+ * set it to <code>false</code> to avoid unnecessary extra code in the final
+ * compiled output.
+ * 
+ * <h3>Example</h3>
+ * A module using this package might look like the following:
+ * 
+ * <pre>
+ * &lt;module>
+ *   &lt;inherits name='com.google.gwt.user.User'/>
+ *   
+ *   &lt;!-- Inheriting 'Debug' on the next line makes the features available. -->
+ *   &lt;inherits name='com.google.gwt.debug.Debug'/>
+ *   
+ *   &lt;!-- Disable for production by uncommenting the next line -->
+ *   &lt;!-- &lt;set-property name="gwt.enableDebugId" value="false"/> -->
+ *   
+ *   &lt;entry-point class='your-entry-point-class'/>
+ * &lt;/module>
+ * </pre>
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.debug.client;
diff --git a/user/src/com/google/gwt/dom/client/DOMImpl.java b/user/src/com/google/gwt/dom/client/DOMImpl.java
index 738849d..dd0c07c 100644
--- a/user/src/com/google/gwt/dom/client/DOMImpl.java
+++ b/user/src/com/google/gwt/dom/client/DOMImpl.java
@@ -25,20 +25,20 @@
     button.click();
   }-*/;
 
-  public native Element createElement(String tag) /*-{
-    return $doc.createElement(tag);
+  public native Element createElement(Document doc, String tag) /*-{
+    return doc.createElement(tag);
   }-*/;
 
   public abstract NativeEvent createHtmlEvent(Document doc, String type, boolean canBubble,
       boolean cancelable);
 
-  public native InputElement createInputElement(String type) /*-{
-    var e = $doc.createElement("INPUT");
+  public native InputElement createInputElement(Document doc, String type) /*-{
+    var e = doc.createElement("INPUT");
     e.type = type;
     return e;
   }-*/;
 
-  public abstract InputElement createInputRadioElement(String name);
+  public abstract InputElement createInputRadioElement(Document doc, String name);
 
   public abstract NativeEvent createKeyEvent(Document doc, String type,
       boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey,
@@ -49,14 +49,14 @@
       int screenY, int clientX, int clientY, boolean ctrlKey, boolean altKey,
       boolean shiftKey, boolean metaKey, int button, Element relatedTarget);
 
-  public ScriptElement createScriptElement(String source) {
-    ScriptElement elem = (ScriptElement) createElement("script");
+  public ScriptElement createScriptElement(Document doc, String source) {
+    ScriptElement elem = (ScriptElement) createElement(doc, "script");
     elem.setText(source);
     return elem;
   }
 
-  public SelectElement createSelectElement(boolean multiple) {
-    SelectElement select = (SelectElement) createElement("select");
+  public SelectElement createSelectElement(Document doc, boolean multiple) {
+    SelectElement select = (SelectElement) createElement(doc, "select");
     if (multiple) {
       select.setMultiple(true);
     }
@@ -160,11 +160,15 @@
     return top;
   }-*/;
 
-  public native int getBodyOffsetLeft() /*-{
+  public native String getAttribute(Element elem, String name) /*-{
+    return elem.getAttribute(name) || '';
+  }-*/;
+
+  public native int getBodyOffsetLeft(Document doc) /*-{
     return 0;
   }-*/;
 
-  public native int getBodyOffsetTop() /*-{
+  public native int getBodyOffsetTop(Document doc) /*-{
     return 0;
   }-*/;
 
@@ -212,6 +216,14 @@
     return parent;
   }-*/;
 
+  public int getScrollLeft(Document doc) {
+    return doc.getViewportElement().getScrollLeft();
+  }
+
+  public int getScrollTop(Document doc) {
+    return doc.getViewportElement().getScrollTop();
+  }
+
   public native String imgGetSrc(Element img) /*-{
     return img.src;
   }-*/;
@@ -286,10 +298,18 @@
     }
     // Add a new text node.
     if (text != null) {
-      elem.appendChild($doc.createTextNode(text));
+      elem.appendChild(elem.ownerDocument.createTextNode(text));
     }
   }-*/;
 
+  public void setScrollLeft(Document doc, int left) {
+    doc.getViewportElement().setScrollLeft(left);
+  }
+
+  public void setScrollTop(Document doc, int top) {
+    doc.getViewportElement().setScrollTop(top);
+  }
+
   public native String toString(Element elem) /*-{
     return elem.outerHTML;
   }-*/;
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE6.java b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
index f691ee5..d0c2f21 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplIE6.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
@@ -32,8 +32,8 @@
   }-*/;
 
   @Override
-  public native InputElement createInputRadioElement(String name) /*-{
-    return $doc.createElement("<INPUT type='RADIO' name='" + name + "'>");
+  public native InputElement createInputRadioElement(Document doc, String name) /*-{
+    return doc.createElement("<INPUT type='RADIO' name='" + name + "'>");
   }-*/; 
 
   @Override
@@ -91,9 +91,9 @@
    * but it should be harmless.
    */
   @Override
-  public native SelectElement createSelectElement(boolean multiple) /*-{
+  public native SelectElement createSelectElement(Document doc, boolean multiple) /*-{
     var html = multiple ? "<SELECT MULTIPLE>" : "<SELECT>"; 
-    return $doc.createElement(html);
+    return doc.createElement(html);
   }-*/;
 
   public native void dispatchEvent(Element target, NativeEvent evt) /*-{
@@ -135,40 +135,39 @@
   }-*/;
 
   @Override
-  public native int getAbsoluteLeft(Element elem) /*-{
-    // getBoundingClientRect() throws a JS exception if the elem is not attached
-    // to the document, so we wrap it in a try/catch block
-    try {
-      return Math.floor((elem.getBoundingClientRect().left /
-        this.@com.google.gwt.dom.client.DOMImplIE6::getZoomMultiple()()) +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft);
-    } catch (e) {
-      return 0;
-    }
+  public int getAbsoluteLeft(Element elem) {
+    Document doc = elem.getOwnerDocument();
+    return (int) Math.floor(getBoundingClientRectLeft(elem)
+        / getZoomMultiple(doc) + doc.getScrollLeft());
+  }
+
+  @Override
+  public int getAbsoluteTop(Element elem) {
+    Document doc = elem.getOwnerDocument();
+    return (int) Math.floor(getBoundingClientRectTop(elem)
+        / getZoomMultiple(doc) + doc.getScrollTop());
+  }
+
+  /**
+   * IE returns a numeric type for some attributes that are really properties,
+   * such as offsetWidth.  We need to coerce these to strings to prevent a
+   * runtime JS exception.
+   */
+  @Override
+  public native String getAttribute(Element elem, String name) /*-{
+    var attr = elem.getAttribute(name);
+    return attr == null? '' : attr + '';
   }-*/;
 
   @Override
-  public native int getAbsoluteTop(Element elem) /*-{
-    // getBoundingClientRect() throws a JS exception if the elem is not attached
-    // to the document, so we wrap it in a try/catch block
-    try {
-      return Math.floor((elem.getBoundingClientRect().top /
-        this.@com.google.gwt.dom.client.DOMImplIE6::getZoomMultiple()()) +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop);
-    } catch (e) {
-      return 0;
-    }
-  }-*/;
+  public int getBodyOffsetLeft(Document doc) {
+    return getClientLeft(doc.getViewportElement());
+  }
 
   @Override
-  public native int getBodyOffsetLeft() /*-{
-    return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.clientLeft;
-  }-*/;
-
-  @Override
-  public native int getBodyOffsetTop() /*-{
-    return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.clientTop;
-  }-*/;
+  public int getBodyOffsetTop(Document doc) {
+    return getClientTop(doc.getViewportElement());
+  }
 
   @Override
   public native String getInnerText(Element elem) /*-{
@@ -224,6 +223,40 @@
     elem.innerText = text || '';
   }-*/;
 
+  private native int getBoundingClientRectLeft(Element elem) /*-{
+    // getBoundingClientRect() throws a JS exception if the elem is not attached
+    // to the document, so we wrap it in a try/catch block
+    try {
+      return elem.getBoundingClientRect().left;
+    } catch (e) {
+      return 0;
+    }
+  }-*/;
+
+  private native int getBoundingClientRectTop(Element elem) /*-{
+    // getBoundingClientRect() throws a JS exception if the elem is not attached
+    // to the document, so we wrap it in a try/catch block
+    try {
+      return elem.getBoundingClientRect().top;
+    } catch (e) {
+      return 0;
+    }
+  }-*/;
+
+  /**
+   * clientLeft is non-standard and not implemented on all browsers.
+   */
+  private native int getClientLeft(Element elem) /*-{
+    return elem.clientLeft;
+  }-*/;
+
+  /**
+   * clientTop is non-standard and not implemented on all browsers.
+   */
+  private native int getClientTop(Element elem) /*-{
+    return elem.clientTop;
+  }-*/;
+
   /**
    * Get the zoom multiple based on the current IE zoom level. A multiple of 2.0
    * means that the user has zoomed in to 200%.
@@ -231,11 +264,12 @@
    * @return the zoom multiple
    */
   @SuppressWarnings("unused")
-  private native double getZoomMultiple() /*-{
-    if ($doc.compatMode == 'CSS1Compat') {
+  private double getZoomMultiple(Document doc) {
+    if (doc.getCompatMode().equals("CSS1Compat")) {
       return 1;
     } else {
-      return $doc.body.parentElement.offsetWidth / $doc.body.offsetWidth;
+      return doc.getBody().getParentElement().getOffsetWidth() /
+        doc.getBody().getOffsetWidth();
     }
-  }-*/;
+  }
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
index 48b9c77..7465736 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -20,6 +20,7 @@
  */
 class DOMImplMozilla extends DOMImplStandard {
 
+  @Override
   public native void buttonClick(ButtonElement button) /*-{
     var doc = button.ownerDocument;
     if (doc != null) {
@@ -36,58 +37,26 @@
   }-*/;
 
   @Override
-  public native int getAbsoluteLeft(Element elem) /*-{
-    // Firefox 3 is actively throwing errors when getBoxObjectFor() is called,
-    // so we use getBoundingClientRect() whenever possible (but it's not
-    // supported on older versions). If changing this code, make sure to check
-    // the museum entry for issue 1932.
-    // (x) | 0 is used to coerce the value to an integer
-    if (Element.prototype.getBoundingClientRect) {
-      return (elem.getBoundingClientRect().left +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft) | 0;
-    } else {
-      // We cannot use DOMImpl here because offsetLeft/Top return erroneous
-      // values when overflow is not visible.  We have to difference screenX
-      // here due to a change in getBoxObjectFor which causes inconsistencies
-      // on whether the calculations are inside or outside of the element's
-      // border.
-      // If the element is in a scrollable div, getBoxObjectFor(elem) can return
-      // a value that varies by 1 pixel.
-      return $doc.getBoxObjectFor(elem).screenX -
-        $doc.getBoxObjectFor($doc.documentElement).screenX;
-    }
-  }-*/;
+  public int getAbsoluteLeft(Element elem) {
+    return getAbsoluteLeftImpl(elem.getOwnerDocument().getViewportElement(),
+        elem);
+  }
 
   @Override
-  public native int getAbsoluteTop(Element elem) /*-{
-    // Firefox 3 is actively throwing errors when getBoxObjectFor() is called,
-    // so we use getBoundingClientRect() whenever possible (but it's not
-    // supported on older versions). If changing this code, make sure to check
-    // the museum entry for issue 1932.
-    // (x) | 0 is used to coerce the value to an integer
-    if (Element.prototype.getBoundingClientRect) {
-      return (elem.getBoundingClientRect().top +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop) | 0;
-    } else {
-      // We cannot use DOMImpl here because offsetLeft/Top return erroneous
-      // values when overflow is not visible.  We have to difference screenX
-      // here due to a change in getBoxObjectFor which causes inconsistencies
-      // on whether the calculations are inside or outside of the element's
-      // border.
-      return $doc.getBoxObjectFor(elem).screenY -
-        $doc.getBoxObjectFor($doc.documentElement).screenY;
-    }
-  }-*/;
+  public int getAbsoluteTop(Element elem) {
+    return getAbsoluteTopImpl(elem.getOwnerDocument().getViewportElement(),
+        elem);
+  }
 
   @Override
-  public native int getBodyOffsetLeft() /*-{
-    var style = $wnd.getComputedStyle($doc.documentElement, '');
+  public native int getBodyOffsetLeft(Document doc) /*-{
+    var style = $wnd.getComputedStyle(doc.documentElement, '');
     return parseInt(style.marginLeft) + parseInt(style.borderLeftWidth);
   }-*/;
 
   @Override
-  public native int getBodyOffsetTop() /*-{
-    var style = $wnd.getComputedStyle($doc.documentElement, '');
+  public native int getBodyOffsetTop(Document doc) /*-{
+    var style = $wnd.getComputedStyle(doc.documentElement, '');
     return parseInt(style.marginTop) + parseInt(style.borderTopWidth);
   }-*/;
 
@@ -112,11 +81,54 @@
   public native String toString(Element elem) /*-{
     // Basic idea is to use the innerHTML property by copying the node into a
     // div and getting the innerHTML
+    var doc = elem.ownerDocument;
     var temp = elem.cloneNode(true);
-    var tempDiv = $doc.createElement("DIV");
+    var tempDiv = doc.createElement("DIV");
     tempDiv.appendChild(temp);
     outer = tempDiv.innerHTML;
     temp.innerHTML = "";
     return outer;
   }-*/;
+
+  private native int getAbsoluteLeftImpl(Element viewport, Element elem) /*-{
+    // Firefox 3 is actively throwing errors when getBoxObjectFor() is called,
+    // so we use getBoundingClientRect() whenever possible (but it's not
+    // supported on older versions). If changing this code, make sure to check
+    // the museum entry for issue 1932.
+    // (x) | 0 is used to coerce the value to an integer
+    if (Element.prototype.getBoundingClientRect) {
+      return (elem.getBoundingClientRect().left + viewport.scrollLeft) | 0;
+    } else {
+      // We cannot use DOMImpl here because offsetLeft/Top return erroneous
+      // values when overflow is not visible.  We have to difference screenX
+      // here due to a change in getBoxObjectFor which causes inconsistencies
+      // on whether the calculations are inside or outside of the element's
+      // border.
+      // If the element is in a scrollable div, getBoxObjectFor(elem) can return
+      // a value that varies by 1 pixel.
+      var doc = elem.ownerDocument;
+      return doc.getBoxObjectFor(elem).screenX -
+        doc.getBoxObjectFor(doc.documentElement).screenX;
+    }
+  }-*/;
+
+  private native int getAbsoluteTopImpl(Element viewport, Element elem) /*-{
+    // Firefox 3 is actively throwing errors when getBoxObjectFor() is called,
+    // so we use getBoundingClientRect() whenever possible (but it's not
+    // supported on older versions). If changing this code, make sure to check
+    // the museum entry for issue 1932.
+    // (x) | 0 is used to coerce the value to an integer
+    if (Element.prototype.getBoundingClientRect) {
+      return (elem.getBoundingClientRect().top + viewport.scrollTop) | 0;
+    } else {
+      // We cannot use DOMImpl here because offsetLeft/Top return erroneous
+      // values when overflow is not visible.  We have to difference screenX
+      // here due to a change in getBoxObjectFor which causes inconsistencies
+      // on whether the calculations are inside or outside of the element's
+      // border.
+      var doc = elem.ownerDocument;
+      return doc.getBoxObjectFor(elem).screenY -
+        doc.getBoxObjectFor(doc.documentElement).screenY;
+    }
+  }-*/;
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozillaOld.java b/user/src/com/google/gwt/dom/client/DOMImplMozillaOld.java
index b4e375b..64184a9 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplMozillaOld.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplMozillaOld.java
@@ -26,11 +26,12 @@
 
   @Override
   public native int getAbsoluteLeft(Element elem) /*-{
-    var style = $doc.defaultView.getComputedStyle(elem, null);
-    var left = $doc.getBoxObjectFor(elem).x - Math.round(
+    var doc = elem.ownerDocument;
+    var style = doc.defaultView.getComputedStyle(elem, null);
+    var left = doc.getBoxObjectFor(elem).x - Math.round(
         style.getPropertyCSSValue('border-left-width').getFloatValue(
         CSSPrimitiveValue.CSS_PX));
-        
+
     var parent = elem.parentNode;
     while (parent) {
       // Sometimes get NAN.
@@ -46,8 +47,9 @@
 
   @Override
   public native int getAbsoluteTop(Element elem) /*-{
-    var style = $doc.defaultView.getComputedStyle(elem, null);
-    var top = $doc.getBoxObjectFor(elem).y - Math.round(
+    var doc = elem.ownerDocument;
+    var style = doc.defaultView.getComputedStyle(elem, null);
+    var top = doc.getBoxObjectFor(elem).y - Math.round(
         style.getPropertyCSSValue('border-top-width').getFloatValue(
         CSSPrimitiveValue.CSS_PX));
       
@@ -89,8 +91,7 @@
     }
     // Add a new text node.
     if (text != null) {
-      elem.appendChild($doc.createTextNode(text));
+      elem.appendChild(elem.ownerDocument.createTextNode(text));
     }
   }-*/;
-
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplSafari.java b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
index 69a9223..ca512ac 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplSafari.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
@@ -41,45 +41,13 @@
    * Safari 2 does not support {@link ScriptElement#setText(String)}.
    */
   @Override
-  public ScriptElement createScriptElement(String source) {
-    ScriptElement elem = (ScriptElement) createElement("script");
+  public ScriptElement createScriptElement(Document doc, String source) {
+    ScriptElement elem = (ScriptElement) createElement(doc, "script");
     elem.setInnerText(source);
     return elem;
   }
 
   @Override
-  public native int eventGetClientX(NativeEvent evt) /*-{
-    // In Safari2: clientX is wrong and pageX is returned instead.
-    // $wnd.devicePixelRatio identifies Safari 3 from Safari 2
-    if ($wnd.devicePixelRatio) {
-      return evt.clientX || 0;
-    } else {
-      // Subtract the margin and border of the HTML element in Safari 2 
-      // TODO: Remove this code when we drop Safari 2 support
-      var style = document.defaultView.getComputedStyle($doc.getElementsByTagName('html')[0], '');
-      return evt.pageX - $doc.body.scrollLeft
-          - parseInt(style.getPropertyValue('margin-left'))
-          - parseInt(style.getPropertyValue('border-left-width')) || 0;
-    }
-  }-*/;
-
-  @Override
-  public native int eventGetClientY(NativeEvent evt) /*-{
-    // In Safari2: clientY is wrong and pageY is returned instead.
-    // $wnd.devicePixelRatio identifies Safari 3 from Safari 2
-    if ($wnd.devicePixelRatio) {
-      return evt.clientY || 0;
-    } else {
-      // Subtract the margin and border of the HTML element in Safari 2 
-      // TODO: Remove this code when we drop Safari 2 support
-      var style = document.defaultView.getComputedStyle($doc.getElementsByTagName('html')[0], '');
-      return evt.pageY - $doc.body.scrollTop
-          - parseInt(style.getPropertyValue('margin-top'))
-          - parseInt(style.getPropertyValue('border-top-width')) || 0;
-    }
-  }-*/;
-
-  @Override
   public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{
     return Math.round(-evt.wheelDelta / 40) || 0;
   }-*/;
@@ -93,6 +61,7 @@
     }
 
     var left = 0;
+    var doc = elem.ownerDocument;
     var curr = elem.parentNode;
     if (curr) {
       // This intentionally excludes body which has a null offsetParent.
@@ -102,7 +71,7 @@
         // In RTL mode, offsetLeft is relative to the left edge of the
         // scrollable area when scrolled all the way to the right, so we need
         // to add back that difference.
-        if ($doc.defaultView.getComputedStyle(curr, '').getPropertyValue('direction') == 'rtl') {
+        if (doc.defaultView.getComputedStyle(curr, '').getPropertyValue('direction') == 'rtl') {
           left += (curr.scrollWidth - curr.clientWidth);
         }
 
@@ -117,7 +86,7 @@
       // the borders of the parent manually.
       var parent = elem.offsetParent;
       if (parent && $wnd.devicePixelRatio) {
-        left += parseInt($doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-left-width'));
+        left += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-left-width'));
       }
 
       // Safari bug: a top-level absolutely positioned element includes the
@@ -141,6 +110,7 @@
     }
 
     var top = 0;
+    var doc = elem.ownerDocument;
     var curr = elem.parentNode;
     if (curr) {
       // This intentionally excludes body which has a null offsetParent.
@@ -157,7 +127,7 @@
       // borders of the parent manually.
       var parent = elem.offsetParent;
       if (parent && $wnd.devicePixelRatio) {
-        top += parseInt($doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-top-width'));
+        top += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-top-width'));
       }
 
       // Safari bug: a top-level absolutely positioned element includes the
@@ -173,6 +143,20 @@
   }-*/;
 
   @Override
+  public int getScrollLeft(Document doc) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    return doc.getBody().getScrollLeft();
+  }
+
+  @Override
+  public int getScrollTop(Document doc) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    return doc.getBody().getScrollTop();
+  }
+
+  @Override
   public native boolean isOrHasChild(Element parent, Element child) /*-{
     while (child) {
       if (parent == child) {
@@ -215,4 +199,18 @@
   public native void selectRemoveOption(SelectElement select, int index) /*-{
     select.removeChild(select.children[index]);
   }-*/;
+
+  @Override
+  public void setScrollLeft(Document doc, int left) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    doc.getBody().setScrollLeft(left);
+  }
+
+  @Override
+  public void setScrollTop(Document doc, int top) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    doc.getBody().setScrollTop(top);
+  }
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplStandard.java b/user/src/com/google/gwt/dom/client/DOMImplStandard.java
index 0536d46..c1cd284 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplStandard.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplStandard.java
@@ -32,8 +32,8 @@
   }-*/;
 
   @Override
-  public native InputElement createInputRadioElement(String name) /*-{
-    var elem = $doc.createElement("INPUT");
+  public native InputElement createInputRadioElement(Document doc, String name) /*-{
+    var elem = doc.createElement("INPUT");
     elem.type = 'radio';
     elem.name = name;
     return elem;
diff --git a/user/src/com/google/gwt/dom/client/Document.java b/user/src/com/google/gwt/dom/client/Document.java
index 4c53f3a..209b1b9 100644
--- a/user/src/com/google/gwt/dom/client/Document.java
+++ b/user/src/com/google/gwt/dom/client/Document.java
@@ -41,7 +41,7 @@
    * @return the newly created element
    */
   public final AnchorElement createAnchorElement() {
-    return (AnchorElement) DOMImpl.impl.createElement(AnchorElement.TAG);
+    return (AnchorElement) DOMImpl.impl.createElement(this, AnchorElement.TAG);
   }
 
   /**
@@ -50,7 +50,7 @@
    * @return the newly created element
    */
   public final AreaElement createAreaElement() {
-    return (AreaElement) DOMImpl.impl.createElement(AreaElement.TAG);
+    return (AreaElement) DOMImpl.impl.createElement(this, AreaElement.TAG);
   }
 
   /**
@@ -59,7 +59,7 @@
    * @return the newly created element
    */
   public final BaseElement createBaseElement() {
-    return (BaseElement) DOMImpl.impl.createElement(BaseElement.TAG);
+    return (BaseElement) DOMImpl.impl.createElement(this, BaseElement.TAG);
   }
 
   /**
@@ -68,7 +68,7 @@
    * @return the newly created element
    */
   public final QuoteElement createBlockQuoteElement() {
-    return (QuoteElement) DOMImpl.impl.createElement(QuoteElement.TAG_BLOCKQUOTE);
+    return (QuoteElement) DOMImpl.impl.createElement(this, QuoteElement.TAG_BLOCKQUOTE);
   }
 
   /**
@@ -84,7 +84,7 @@
    * @return the newly created element
    */
   public final BRElement createBRElement() {
-    return (BRElement) DOMImpl.impl.createElement(BRElement.TAG);
+    return (BRElement) DOMImpl.impl.createElement(this, BRElement.TAG);
   }
 
   /**
@@ -93,7 +93,7 @@
    * @return the newly created element
    */
   public final ButtonElement createButtonElement() {
-    return (ButtonElement) DOMImpl.impl.createElement(ButtonElement.TAG);
+    return (ButtonElement) DOMImpl.impl.createElement(this, ButtonElement.TAG);
   }
 
   /**
@@ -102,7 +102,7 @@
    * @return the newly created element
    */
   public final TableCaptionElement createCaptionElement() {
-    return (TableCaptionElement) DOMImpl.impl.createElement(TableCaptionElement.TAG);
+    return (TableCaptionElement) DOMImpl.impl.createElement(this, TableCaptionElement.TAG);
   }
 
   /**
@@ -118,7 +118,7 @@
    * @return the newly created element
    */
   public final InputElement createCheckInputElement() {
-    return DOMImpl.impl.createInputElement("checkbox");
+    return DOMImpl.impl.createInputElement(this, "checkbox");
   }
 
   /**
@@ -156,7 +156,7 @@
    * @return the newly created element
    */
   public final TableColElement createColElement() {
-    return (TableColElement) DOMImpl.impl.createElement(TableColElement.TAG_COL);
+    return (TableColElement) DOMImpl.impl.createElement(this, TableColElement.TAG_COL);
   }
 
   /**
@@ -165,7 +165,7 @@
    * @return the newly created element
    */
   public final TableColElement createColGroupElement() {
-    return (TableColElement) DOMImpl.impl.createElement(TableColElement.TAG_COLGROUP);
+    return (TableColElement) DOMImpl.impl.createElement(this, TableColElement.TAG_COLGROUP);
   }
 
   /**
@@ -220,7 +220,7 @@
    * @return the newly created element
    */
   public final ModElement createDelElement() {
-    return (ModElement) DOMImpl.impl.createElement(ModElement.TAG_DEL);
+    return (ModElement) DOMImpl.impl.createElement(this, ModElement.TAG_DEL);
   }
 
   /**
@@ -229,7 +229,7 @@
    * @return the newly created element
    */
   public final DivElement createDivElement() {
-    return (DivElement) DOMImpl.impl.createElement(DivElement.TAG);
+    return (DivElement) DOMImpl.impl.createElement(this, DivElement.TAG);
   }
 
   /**
@@ -238,7 +238,7 @@
    * @return the newly created element
    */
   public final DListElement createDLElement() {
-    return (DListElement) DOMImpl.impl.createElement(DListElement.TAG);
+    return (DListElement) DOMImpl.impl.createElement(this, DListElement.TAG);
   }
 
   /**
@@ -248,7 +248,7 @@
    * @return the newly created element
    */
   public final Element createElement(String tagName) {
-    return DOMImpl.impl.createElement(tagName);
+    return DOMImpl.impl.createElement(this, tagName);
   }
 
   /**
@@ -266,7 +266,7 @@
    * @return the newly created element
    */
   public final FieldSetElement createFieldSetElement() {
-    return (FieldSetElement) DOMImpl.impl.createElement(FieldSetElement.TAG);
+    return (FieldSetElement) DOMImpl.impl.createElement(this, FieldSetElement.TAG);
   }
 
   /**
@@ -275,7 +275,7 @@
    * @return the newly created element
    */
   public final InputElement createFileInputElement() {
-    return DOMImpl.impl.createInputElement("file");
+    return DOMImpl.impl.createInputElement(this, "file");
   }
 
   /**
@@ -293,7 +293,7 @@
    * @return the newly created element
    */
   public final FormElement createFormElement() {
-    return (FormElement) DOMImpl.impl.createElement(FormElement.TAG);
+    return (FormElement) DOMImpl.impl.createElement(this, FormElement.TAG);
   }
 
   /**
@@ -302,7 +302,7 @@
    * @return the newly created element
    */
   public final FrameElement createFrameElement() {
-    return (FrameElement) DOMImpl.impl.createElement(FrameElement.TAG);
+    return (FrameElement) DOMImpl.impl.createElement(this, FrameElement.TAG);
   }
 
   /**
@@ -311,7 +311,7 @@
    * @return the newly created element
    */
   public final FrameSetElement createFrameSetElement() {
-    return (FrameSetElement) DOMImpl.impl.createElement(FrameSetElement.TAG);
+    return (FrameSetElement) DOMImpl.impl.createElement(this, FrameSetElement.TAG);
   }
 
   /**
@@ -320,7 +320,7 @@
    * @return the newly created element
    */
   public final HeadElement createHeadElement() {
-    return (HeadElement) DOMImpl.impl.createElement(HeadElement.TAG);
+    return (HeadElement) DOMImpl.impl.createElement(this, HeadElement.TAG);
   }
 
   /**
@@ -331,7 +331,7 @@
    */
   public final HeadingElement createHElement(int n) {
     assert (n >= 1) && (n <= 6);
-    return (HeadingElement) DOMImpl.impl.createElement("h" + n);
+    return (HeadingElement) DOMImpl.impl.createElement(this, "h" + n);
   }
 
   /**
@@ -340,7 +340,7 @@
    * @return the newly created element
    */
   public final InputElement createHiddenInputElement() {
-    return DOMImpl.impl.createInputElement("hidden");
+    return DOMImpl.impl.createInputElement(this, "hidden");
   }
 
   /**
@@ -349,7 +349,7 @@
    * @return the newly created element
    */
   public final HRElement createHRElement() {
-    return (HRElement) DOMImpl.impl.createElement(HRElement.TAG);
+    return (HRElement) DOMImpl.impl.createElement(this, HRElement.TAG);
   }
 
   /**
@@ -383,7 +383,7 @@
    * @return the newly created element
    */
   public final IFrameElement createIFrameElement() {
-    return (IFrameElement) DOMImpl.impl.createElement(IFrameElement.TAG);
+    return (IFrameElement) DOMImpl.impl.createElement(this, IFrameElement.TAG);
   }
 
   /**
@@ -392,7 +392,7 @@
    * @return the newly created element
    */
   public final ImageElement createImageElement() {
-    return (ImageElement) DOMImpl.impl.createElement(ImageElement.TAG);
+    return (ImageElement) DOMImpl.impl.createElement(this, ImageElement.TAG);
   }
 
   /**
@@ -401,7 +401,7 @@
    * @return the newly created element
    */
   public final InputElement createImageInputElement() {
-    return DOMImpl.impl.createInputElement("image");
+    return DOMImpl.impl.createInputElement(this, "image");
   }
 
   /**
@@ -410,7 +410,7 @@
    * @return the newly created element
    */
   public final ModElement createInsElement() {
-    return (ModElement) DOMImpl.impl.createElement(ModElement.TAG_INS);
+    return (ModElement) DOMImpl.impl.createElement(this, ModElement.TAG_INS);
   }
 
   /**
@@ -504,7 +504,7 @@
    * @return the newly created element
    */
   public final LabelElement createLabelElement() {
-    return (LabelElement) DOMImpl.impl.createElement(LabelElement.TAG);
+    return (LabelElement) DOMImpl.impl.createElement(this, LabelElement.TAG);
   }
 
   /**
@@ -513,7 +513,7 @@
    * @return the newly created element
    */
   public final LegendElement createLegendElement() {
-    return (LegendElement) DOMImpl.impl.createElement(LegendElement.TAG);
+    return (LegendElement) DOMImpl.impl.createElement(this, LegendElement.TAG);
   }
 
   /**
@@ -522,7 +522,7 @@
    * @return the newly created element
    */
   public final LIElement createLIElement() {
-    return (LIElement) DOMImpl.impl.createElement(LIElement.TAG);
+    return (LIElement) DOMImpl.impl.createElement(this, LIElement.TAG);
   }
 
   /**
@@ -531,7 +531,7 @@
    * @return the newly created element
    */
   public final LinkElement createLinkElement() {
-    return (LinkElement) DOMImpl.impl.createElement(LinkElement.TAG);
+    return (LinkElement) DOMImpl.impl.createElement(this, LinkElement.TAG);
   }
 
   /**
@@ -549,7 +549,7 @@
    * @return the newly created element
    */
   public final MapElement createMapElement() {
-    return (MapElement) DOMImpl.impl.createElement(MapElement.TAG);
+    return (MapElement) DOMImpl.impl.createElement(this, MapElement.TAG);
   }
 
   /**
@@ -558,7 +558,7 @@
    * @return the newly created element
    */
   public final MetaElement createMetaElement() {
-    return (MetaElement) DOMImpl.impl.createElement(MetaElement.TAG);
+    return (MetaElement) DOMImpl.impl.createElement(this, MetaElement.TAG);
   }
 
   /**
@@ -735,7 +735,7 @@
    * @return the newly created element
    */
   public final ObjectElement createObjectElement() {
-    return (ObjectElement) DOMImpl.impl.createElement(ObjectElement.TAG);
+    return (ObjectElement) DOMImpl.impl.createElement(this, ObjectElement.TAG);
   }
 
   /**
@@ -744,7 +744,7 @@
    * @return the newly created element
    */
   public final OListElement createOLElement() {
-    return (OListElement) DOMImpl.impl.createElement(OListElement.TAG);
+    return (OListElement) DOMImpl.impl.createElement(this, OListElement.TAG);
   }
 
   /**
@@ -753,7 +753,7 @@
    * @return the newly created element
    */
   public final OptGroupElement createOptGroupElement() {
-    return (OptGroupElement) DOMImpl.impl.createElement(OptGroupElement.TAG);
+    return (OptGroupElement) DOMImpl.impl.createElement(this, OptGroupElement.TAG);
   }
 
   /**
@@ -762,7 +762,7 @@
    * @return the newly created element
    */
   public final OptionElement createOptionElement() {
-    return (OptionElement) DOMImpl.impl.createElement(OptionElement.TAG);
+    return (OptionElement) DOMImpl.impl.createElement(this, OptionElement.TAG);
   }
 
   /**
@@ -771,7 +771,7 @@
    * @return the newly created element
    */
   public final ParamElement createParamElement() {
-    return (ParamElement) DOMImpl.impl.createElement(ParamElement.TAG);
+    return (ParamElement) DOMImpl.impl.createElement(this, ParamElement.TAG);
   }
 
   /**
@@ -780,7 +780,7 @@
    * @return the newly created element
    */
   public final InputElement createPasswordInputElement() {
-    return DOMImpl.impl.createInputElement("password");
+    return DOMImpl.impl.createInputElement(this, "password");
   }
 
   /**
@@ -789,7 +789,7 @@
    * @return the newly created element
    */
   public final ParagraphElement createPElement() {
-    return (ParagraphElement) DOMImpl.impl.createElement(ParagraphElement.TAG);
+    return (ParagraphElement) DOMImpl.impl.createElement(this, ParagraphElement.TAG);
   }
 
   /**
@@ -798,7 +798,7 @@
    * @return the newly created element
    */
   public final PreElement createPreElement() {
-    return (PreElement) DOMImpl.impl.createElement(PreElement.TAG);
+    return (PreElement) DOMImpl.impl.createElement(this, PreElement.TAG);
   }
 
   /**
@@ -807,7 +807,7 @@
    * @return the newly created element
    */
   public final QuoteElement createQElement() {
-    return (QuoteElement) DOMImpl.impl.createElement(QuoteElement.TAG_Q);
+    return (QuoteElement) DOMImpl.impl.createElement(this, QuoteElement.TAG_Q);
   }
 
   /**
@@ -817,7 +817,7 @@
    * @return the newly created element
    */
   public final InputElement createRadioInputElement(String name) {
-    return DOMImpl.impl.createInputRadioElement(name);
+    return DOMImpl.impl.createInputRadioElement(this, name);
   }
 
   /**
@@ -826,7 +826,7 @@
    * @return the newly created element
    */
   public final ScriptElement createScriptElement() {
-    return (ScriptElement) DOMImpl.impl.createElement(ScriptElement.TAG);
+    return (ScriptElement) DOMImpl.impl.createElement(this, ScriptElement.TAG);
   }
 
   /**
@@ -836,7 +836,7 @@
    * @return the newly created element
    */
   public final ScriptElement createScriptElement(String source) {
-    return DOMImpl.impl.createScriptElement(source);
+    return DOMImpl.impl.createScriptElement(this, source);
   }
 
   /**
@@ -857,7 +857,7 @@
    * @return the newly created element
    */
   public final SelectElement createSelectElement() {
-    return DOMImpl.impl.createSelectElement(false);
+    return DOMImpl.impl.createSelectElement(this, false);
   }
 
   /**
@@ -867,7 +867,7 @@
    * @return the newly created element
    */
   public final SelectElement createSelectElement(boolean multiple) {
-    return DOMImpl.impl.createSelectElement(multiple);
+    return DOMImpl.impl.createSelectElement(this, multiple);
   }
 
   /**
@@ -876,7 +876,7 @@
    * @return the newly created element
    */
   public final SpanElement createSpanElement() {
-    return (SpanElement) DOMImpl.impl.createElement(SpanElement.TAG);
+    return (SpanElement) DOMImpl.impl.createElement(this, SpanElement.TAG);
   }
 
   /**
@@ -885,7 +885,7 @@
    * @return the newly created element
    */
   public final StyleElement createStyleElement() {
-    return (StyleElement) DOMImpl.impl.createElement(StyleElement.TAG);
+    return (StyleElement) DOMImpl.impl.createElement(this, StyleElement.TAG);
   }
 
   /**
@@ -894,7 +894,7 @@
    * @return the newly created element
    */
   public final TableElement createTableElement() {
-    return (TableElement) DOMImpl.impl.createElement(TableElement.TAG);
+    return (TableElement) DOMImpl.impl.createElement(this, TableElement.TAG);
   }
 
   /**
@@ -903,7 +903,7 @@
    * @return the newly created element
    */
   public final TableSectionElement createTBodyElement() {
-    return (TableSectionElement) DOMImpl.impl.createElement(TableSectionElement.TAG_TBODY);
+    return (TableSectionElement) DOMImpl.impl.createElement(this, TableSectionElement.TAG_TBODY);
   }
 
   /**
@@ -912,7 +912,7 @@
    * @return the newly created element
    */
   public final TableCellElement createTDElement() {
-    return (TableCellElement) DOMImpl.impl.createElement(TableCellElement.TAG_TD);
+    return (TableCellElement) DOMImpl.impl.createElement(this, TableCellElement.TAG_TD);
   }
 
   /**
@@ -921,7 +921,7 @@
    * @return the newly created element
    */
   public final TextAreaElement createTextAreaElement() {
-    return (TextAreaElement) DOMImpl.impl.createElement(TextAreaElement.TAG);
+    return (TextAreaElement) DOMImpl.impl.createElement(this, TextAreaElement.TAG);
   }
 
   /**
@@ -930,7 +930,7 @@
    * @return the newly created element
    */
   public final InputElement createTextInputElement() {
-    return DOMImpl.impl.createInputElement("text");
+    return DOMImpl.impl.createInputElement(this, "text");
   }
 
   /**
@@ -949,7 +949,7 @@
    * @return the newly created element
    */
   public final TableSectionElement createTFootElement() {
-    return (TableSectionElement) DOMImpl.impl.createElement(TableSectionElement.TAG_TFOOT);
+    return (TableSectionElement) DOMImpl.impl.createElement(this, TableSectionElement.TAG_TFOOT);
   }
 
   /**
@@ -958,7 +958,7 @@
    * @return the newly created element
    */
   public final TableSectionElement createTHeadElement() {
-    return (TableSectionElement) DOMImpl.impl.createElement(TableSectionElement.TAG_THEAD);
+    return (TableSectionElement) DOMImpl.impl.createElement(this, TableSectionElement.TAG_THEAD);
   }
 
   /**
@@ -967,7 +967,7 @@
    * @return the newly created element
    */
   public final TableCellElement createTHElement() {
-    return (TableCellElement) DOMImpl.impl.createElement(TableCellElement.TAG_TH);
+    return (TableCellElement) DOMImpl.impl.createElement(this, TableCellElement.TAG_TH);
   }
 
   /**
@@ -976,7 +976,7 @@
    * @return the newly created element
    */
   public final TitleElement createTitleElement() {
-    return (TitleElement) DOMImpl.impl.createElement(TitleElement.TAG);
+    return (TitleElement) DOMImpl.impl.createElement(this, TitleElement.TAG);
   }
 
   /**
@@ -985,7 +985,7 @@
    * @return the newly created element
    */
   public final TableRowElement createTRElement() {
-    return (TableRowElement) DOMImpl.impl.createElement(TableRowElement.TAG);
+    return (TableRowElement) DOMImpl.impl.createElement(this, TableRowElement.TAG);
   }
 
   /**
@@ -994,7 +994,7 @@
    * @return the newly created element
    */
   public final UListElement createULElement() {
-    return (UListElement) DOMImpl.impl.createElement(UListElement.TAG);
+    return (UListElement) DOMImpl.impl.createElement(this, UListElement.TAG);
   }
 
   /**
@@ -1015,6 +1015,16 @@
   }-*/;
 
   /**
+   * Enables or disables scrolling of the document
+   * 
+   * @param enable whether scrolling should be enabled or disabled
+   */
+  public final void enableScrolling(boolean enable) {
+    getViewportElement().getStyle().setProperty("overflow",
+        enable ? "" : "hidden");
+  }
+
+  /**
    * The element that contains the content for the document. In documents with
    * BODY contents, returns the BODY element.
    * 
@@ -1052,7 +1062,7 @@
    * @return the left offset of the body's positioning coordinate system
    */
   public final int getBodyOffsetLeft() {
-    return DOMImpl.impl.getBodyOffsetLeft();
+    return DOMImpl.impl.getBodyOffsetLeft(this);
   }
 
   /**
@@ -1064,10 +1074,47 @@
    * @see #getBodyOffsetLeft()
    */
   public final int getBodyOffsetTop() {
-    return DOMImpl.impl.getBodyOffsetTop();
+    return DOMImpl.impl.getBodyOffsetTop(this);
   }
 
   /**
+   * The height of the document's client area.
+   * 
+   * @return the document's client height
+   */
+  public final int getClientHeight() {
+    return getViewportElement().getClientHeight();
+  }
+
+  /**
+   * The width of the document's client area.
+   * 
+   * @return the document's client width
+   */
+  public final int getClientWidth() {
+    return getViewportElement().getClientWidth();
+  }
+
+  /**
+   * Gets the document's "compatibility mode", typically used for determining
+   * whether the document is in "quirks" or "strict" mode.
+   * 
+   * @return one of "BackCompat" or "CSS1Compat"
+   */
+  public final native String getCompatMode() /*-{
+    return this.compatMode;
+  }-*/;
+
+  /**
+   * Gets the document's element. This is typically the &lt;html&gt; element.
+   * 
+   * @return the document element
+   */
+  public final native Element getDocumentElement() /*-{
+    return this.documentElement;
+  }-*/;
+
+  /**
    * The domain name of the server that served the document, or null if the
    * server cannot be identified by a domain name.
    * 
@@ -1114,6 +1161,42 @@
   }-*/;
 
   /**
+   * The height of the scrollable area of the document.
+   * 
+   * @return the height of the document's scrollable area
+   */
+  public final int getScrollHeight() {
+    return getViewportElement().getScrollHeight();
+  }
+
+  /**
+   * The number of pixels that the document's content is scrolled from the left.
+   * 
+   * @return the document's left scroll position
+   */
+  public final int getScrollLeft() {
+    return DOMImpl.impl.getScrollLeft(this);
+  }
+
+  /**
+   * The number of pixels that the document's content is scrolled from the top.
+   * 
+   * @return the document's top scroll position
+   */
+  public final int getScrollTop() {
+    return DOMImpl.impl.getScrollTop(this);
+  }
+
+  /**
+   * The width of the scrollable area of the document.
+   * 
+   * @return the width of the document's scrollable area
+   */
+  public final int getScrollWidth() {
+    return getViewportElement().getScrollWidth();
+  }
+
+  /**
    * Gets the title of a document as specified by the TITLE element in the head
    * of the document.
    * 
@@ -1158,6 +1241,36 @@
   }-*/;
 
   /**
+   * Determines whether the document's "compatMode" is "CSS1Compat". This is
+   * normally described as "strict" mode.
+   * 
+   * @return <code>true</code> if the document is in CSS1Compat mode
+   */
+  public final boolean isCSS1Compat() {
+    return getCompatMode().equals("CSS1Compat");
+  }
+
+  /**
+   * Sets the number of pixels that the document's content is scrolled from the
+   * left.
+   * 
+   * @param left the document's left scroll position
+   */
+  public final void setScrollLeft(int left) {
+    DOMImpl.impl.setScrollLeft(this, left);
+  }
+
+  /**
+   * Sets the number of pixels that the document's content is scrolled from the
+   * top.
+   * 
+   * @param top the document's top scroll position
+   */
+  public final void setScrollTop(int top) {
+    DOMImpl.impl.setScrollTop(this, top);
+  }
+
+  /**
    * Sets the title of a document as specified by the TITLE element in the head
    * of the document.
    * 
@@ -1166,4 +1279,18 @@
   public final native void setTitle(String title) /*-{
     this.title = title;
   }-*/;
+
+  /**
+   * Gets the document's viewport element. This is the element that should be
+   * used to for scrolling and client-area measurement. In quirks-mode it is the
+   * &lt;body&gt; element, while in standards-mode it is the &lt;html&gt;
+   * element.
+   * 
+   * This is package-protected because the viewport is
+   * 
+   * @return the document's viewport element
+   */
+  final Element getViewportElement() {
+    return isCSS1Compat() ? getDocumentElement() : getBody();
+  }
 }
diff --git a/user/src/com/google/gwt/dom/client/Element.java b/user/src/com/google/gwt/dom/client/Element.java
index 5bcfd0c..f5c9aed 100644
--- a/user/src/com/google/gwt/dom/client/Element.java
+++ b/user/src/com/google/gwt/dom/client/Element.java
@@ -66,15 +66,18 @@
   }
 
   /**
-   * Retrieves an attribute value by name.
+   * Retrieves an attribute value by name.  Attribute support can be
+   * inconsistent across various browsers.  Consider using the accessors in
+   * {@link Element} and its specific subclasses to retrieve attributes and
+   * properties.
    * 
    * @param name The name of the attribute to retrieve
    * @return The Attr value as a string, or the empty string if that attribute
    *         does not have a specified or default value
    */
-  public final native String getAttribute(String name) /*-{
-     return this.getAttribute(name) || '';
-   }-*/;
+  public final String getAttribute(String name) {
+    return DOMImpl.impl.getAttribute(this, name);
+  }
 
   /**
    * The class attribute of the element. This attribute has been renamed due to
@@ -89,6 +92,26 @@
    }-*/;
 
   /**
+   * Returns the inner height of an element in pixels, including padding but not
+   * the horizontal scrollbar height, border, or margin.
+   * 
+   * @return the element's client height
+   */
+  public final native int getClientHeight() /*-{
+    return this.clientHeight;
+  }-*/;
+
+  /**
+   * Returns the inner width of an element in pixels, including padding but not
+   * the vertical scrollbar width, border, or margin.
+   * 
+   * @return the element's client width
+   */
+  public final native int getClientWidth() /*-{
+    return this.clientWidth;
+  }-*/;
+
+  /**
    * Specifies the base direction of directionally neutral text and the
    * directionality of tables.
    */
@@ -250,14 +273,14 @@
    }-*/;
 
   /**
-   * The number of pixels that an element's content is scrolled to the left.
+   * The number of pixels that an element's content is scrolled from the left.
    */
   public final native int getScrollLeft() /*-{
      return this.scrollLeft || 0;
    }-*/;
 
   /**
-   * The number of pixels that an element's content is scrolled to the top.
+   * The number of pixels that an element's content is scrolled from the top.
    */
   public final native int getScrollTop() /*-{
      return this.scrollTop || 0;
diff --git a/user/src/com/google/gwt/event/dom/client/MouseEvent.java b/user/src/com/google/gwt/event/dom/client/MouseEvent.java
index 1357261..e86407e 100644
--- a/user/src/com/google/gwt/event/dom/client/MouseEvent.java
+++ b/user/src/com/google/gwt/event/dom/client/MouseEvent.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.event.dom.client;
 
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.event.shared.EventHandler;
 
 /**
@@ -24,6 +26,7 @@
  * 
  */
 public abstract class MouseEvent<H extends EventHandler> extends DomEvent<H> {
+
   /**
    * Gets the mouse x-position within the browser window's client area.
    * 
@@ -55,6 +58,32 @@
   }
 
   /**
+   * Gets the mouse x-position relative to a given element.
+   * 
+   * @param target the element whose coordinate system is to be used
+   * @return the relative x-position
+   */
+  public int getRelativeX(Element target) {
+    NativeEvent e = getNativeEvent();
+
+    return e.getClientX() - target.getAbsoluteLeft() + target.getScrollLeft() +
+      target.getOwnerDocument().getScrollLeft();
+  }
+
+  /**
+   * Gets the mouse y-position relative to a given element.
+   * 
+   * @param target the element whose coordinate system is to be used
+   * @return the relative y-position
+   */
+  public int getRelativeY(Element target) {
+    NativeEvent e = getNativeEvent();
+
+    return e.getClientY() - target.getAbsoluteTop() + target.getScrollTop() +
+      target.getOwnerDocument().getScrollTop();
+  }
+
+  /**
    * Gets the mouse x-position on the user's display.
    * 
    * @return the mouse x-position
@@ -73,6 +102,24 @@
   }
 
   /**
+   * Gets the mouse x-position relative to the event's target element.
+   * 
+   * @return the relative x-position
+   */
+  public int getTargetX() {
+    return getRelativeX(getNativeEvent().getTarget());
+  }
+
+  /**
+   * Gets the mouse y-position relative to the event's target element.
+   * 
+   * @return the relative y-position
+   */
+  public int getTargetY() {
+    return getRelativeY(getNativeEvent().getTarget());
+  }
+
+  /**
    * Is <code>alt</code> key down.
    * 
    * @return whether the alt key is down
@@ -107,4 +154,4 @@
   public boolean isShiftKeyDown() {
     return getNativeEvent().getShiftKey();
   }
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/gwt/event/dom/client/package-info.java b/user/src/com/google/gwt/event/dom/client/package-info.java
new file mode 100644
index 0000000..b6b0843
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * Types related to DOM events.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.event.dom.client;
diff --git a/user/src/com/google/gwt/event/logical/shared/package-info.java b/user/src/com/google/gwt/event/logical/shared/package-info.java
new file mode 100644
index 0000000..6840892
--- /dev/null
+++ b/user/src/com/google/gwt/event/logical/shared/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * Types related to logical events that do not have direct analogues to DOM
+ * events and which can be used in contexts other than web browsers. Types
+ * within this package do not use JSNI and do not have static (even
+ * indirect) dependencies upon types which use JSNI.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.event.logical.shared;
diff --git a/user/src/com/google/gwt/event/shared/package-info.java b/user/src/com/google/gwt/event/shared/package-info.java
new file mode 100644
index 0000000..74bcfc3
--- /dev/null
+++ b/user/src/com/google/gwt/event/shared/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * Shared infrastructure underlying both browser and non-browser events. Types
+ * within this package do not use JSNI and do not have static (even indirect)
+ * dependencies upon types which use JSNI.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.event.shared;
diff --git a/user/src/com/google/gwt/i18n/client/BidiUtils.java b/user/src/com/google/gwt/i18n/client/BidiUtils.java
index a51a489..ee89859 100644
--- a/user/src/com/google/gwt/i18n/client/BidiUtils.java
+++ b/user/src/com/google/gwt/i18n/client/BidiUtils.java
@@ -15,8 +15,7 @@
  */
 package com.google.gwt.i18n.client;
 
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.DOM;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.i18n.client.HasDirection.Direction;
 
 /**
@@ -50,8 +49,7 @@
    *         <code>DEFAULT</code> if the directionality is not explicitly set
    */
   public static HasDirection.Direction getDirectionOnElement(Element elem) {
-
-    String dirPropertyValue = DOM.getElementProperty(elem, DIR_PROPERTY_NAME);
+    String dirPropertyValue = elem.getPropertyString(DIR_PROPERTY_NAME);
 
     if (DIR_PROPERTY_VALUE_RTL.equalsIgnoreCase(dirPropertyValue)) {
       return HasDirection.Direction.RTL;
@@ -71,15 +69,14 @@
    *                  <code>DEFAULT</code> if the directionality should be removed from the element   
    */
   public static void setDirectionOnElement(Element elem, Direction direction) {
-    
     switch (direction) {            
       case RTL: {
-        DOM.setElementProperty(elem, DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_RTL);
+        elem.setPropertyString(DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_RTL);
         break;
       }
       
       case LTR: {
-        DOM.setElementProperty(elem, DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_LTR);
+        elem.setPropertyString(DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_LTR);
         break;        
       }
       
@@ -87,7 +84,7 @@
         if (getDirectionOnElement(elem) != HasDirection.Direction.DEFAULT) {
           // only clear out the the dir property if it has already been set to something
           // explicitly
-          DOM.setElementProperty(elem, DIR_PROPERTY_NAME, "");
+          elem.setPropertyString(DIR_PROPERTY_NAME, "");
         }
         break;        
       }     
diff --git a/user/src/com/google/gwt/user/DocumentRoot.gwt.xml b/user/src/com/google/gwt/user/DocumentRoot.gwt.xml
index 211eb95..4a79d87 100644
--- a/user/src/com/google/gwt/user/DocumentRoot.gwt.xml
+++ b/user/src/com/google/gwt/user/DocumentRoot.gwt.xml
@@ -19,9 +19,4 @@
 <module>
 	<inherits name="com.google.gwt.core.Core"/>
 	<inherits name="com.google.gwt.user.UserAgent"/>
-
-	<replace-with class="com.google.gwt.user.client.impl.DocumentRootImplSafari">
-		<when-type-is class="com.google.gwt.user.client.impl.DocumentRootImpl"/>
-		<when-property-is name="user.agent" value="safari"/>
-	</replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Window.gwt.xml b/user/src/com/google/gwt/user/Window.gwt.xml
index ec8571b..9511634 100644
--- a/user/src/com/google/gwt/user/Window.gwt.xml
+++ b/user/src/com/google/gwt/user/Window.gwt.xml
@@ -24,14 +24,4 @@
       <when-type-is class="com.google.gwt.user.client.impl.WindowImpl"/>
       <when-property-is name="user.agent" value="ie6"/>
    </replace-with>
-
-	<replace-with class="com.google.gwt.user.client.impl.WindowImplOpera">
-		<when-type-is class="com.google.gwt.user.client.impl.WindowImpl"/>
-		<when-property-is name="user.agent" value="opera"/>
-	</replace-with>
-
-	<replace-with class="com.google.gwt.user.client.impl.WindowImplSafari">
-		<when-type-is class="com.google.gwt.user.client.impl.WindowImpl"/>
-		<when-property-is name="user.agent" value="safari"/>
-	</replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/client/DOM.java b/user/src/com/google/gwt/user/client/DOM.java
index c781bd1..8ba36b5 100644
--- a/user/src/com/google/gwt/user/client/DOM.java
+++ b/user/src/com/google/gwt/user/client/DOM.java
@@ -784,6 +784,17 @@
   }
 
   /**
+   * Gets the {@link EventListener} that will receive events for the given
+   * element. Only one such listener may exist for a single element.
+   * 
+   * @param elem the element whose listener is to be set
+   * @return the element's event listener
+   */
+  public static EventListener getEventListener(Element elem) {
+    return impl.getEventListener(elem);
+  }
+
+  /**
    * Gets the current set of events sunk by a given element.
    * 
    * @param elem the element whose events are to be retrieved
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index 0d633fd..a7e466a 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -424,6 +424,17 @@
   }
 
   /**
+   * Gets the {@link EventListener} that will receive events for the given
+   * element. Only one such listener may exist for a single element.
+   * 
+   * @param elem the element whose listener is to be set
+   * @return the element's event listener
+   */
+  public static EventListener getEventListener(Element elem) {
+    return DOM.getEventListener((com.google.gwt.user.client.Element) elem);
+  }
+
+  /**
    * Gets the current set of events sunk by a given element.
    * 
    * @param elem the element whose events are to be retrieved
diff --git a/user/src/com/google/gwt/user/client/Window.java b/user/src/com/google/gwt/user/client/Window.java
index 9e4338f..872ac69 100644
--- a/user/src/com/google/gwt/user/client/Window.java
+++ b/user/src/com/google/gwt/user/client/Window.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.event.logical.shared.HasCloseHandlers;
@@ -513,7 +514,7 @@
    * @param enable <code>false</code> to disable window scrolling
    */
   public static void enableScrolling(boolean enable) {
-    impl.enableScrolling(enable);
+    Document.get().enableScrolling(enable);
   }
 
   /**
@@ -523,7 +524,7 @@
    * @return the window's client height
    */
   public static int getClientHeight() {
-    return impl.getClientHeight();
+    return Document.get().getClientHeight();
   }
 
   /**
@@ -533,7 +534,7 @@
    * @return the window's client width
    */
   public static int getClientWidth() {
-    return impl.getClientWidth();
+    return Document.get().getClientWidth();
   }
 
   /**
@@ -542,7 +543,7 @@
    * @return window's scroll left
    */
   public static int getScrollLeft() {
-    return impl.getScrollLeft();
+    return Document.get().getScrollLeft();
   }
 
   /**
@@ -551,7 +552,7 @@
    * @return the window's scroll top
    */
   public static int getScrollTop() {
-    return impl.getScrollTop();
+    return Document.get().getScrollTop();
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index a42eaa5..2b31054 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -100,6 +100,10 @@
 
   public abstract int getChildIndex(Element parent, Element child);
 
+  public native EventListener getEventListener(Element elem) /*-{
+    return elem.__listener;
+  }-*/;
+
   public native int getEventsSunk(Element elem) /*-{
     return elem.__eventBits || 0;
   }-*/;
diff --git a/user/src/com/google/gwt/user/client/impl/DocumentRootImpl.java b/user/src/com/google/gwt/user/client/impl/DocumentRootImpl.java
index 1b4cf58..609f352 100644
--- a/user/src/com/google/gwt/user/client/impl/DocumentRootImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DocumentRootImpl.java
@@ -16,18 +16,23 @@
 package com.google.gwt.user.client.impl;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.user.client.Element;
 
 /**
  * Native implementation used by {@link WindowImpl} and {@link DOMImpl} to
  * access the appropriate documentRoot element, which varies based on the render
  * mode of the browser.
+ * 
+ * @deprecated use the direct methods provided in {@link Document} instead
  */
+@Deprecated
 public class DocumentRootImpl {
   protected static Element documentRoot =
     ((DocumentRootImpl) GWT.create(DocumentRootImpl.class)).getDocumentRoot();
 
-  protected native Element getDocumentRoot() /*-{
-    return $doc.compatMode == 'CSS1Compat' ? $doc.documentElement : $doc.body;
-  }-*/;
+  protected Element getDocumentRoot() {
+    Document doc = Document.get();
+    return (doc.isCSS1Compat() ? doc.getDocumentElement() : doc.getBody()).cast();
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DocumentRootImplSafari.java b/user/src/com/google/gwt/user/client/impl/DocumentRootImplSafari.java
deleted file mode 100644
index f88c4ab..0000000
--- a/user/src/com/google/gwt/user/client/impl/DocumentRootImplSafari.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2008 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.impl;
-
-import com.google.gwt.user.client.Element;
-
-/**
- * Safari implementation of {@link DocumentRootImpl}.
- */
-public class DocumentRootImplSafari extends DocumentRootImpl {
-  @Override
-  protected native Element getDocumentRoot() /*-{
-    // Safari does not implement $doc.compatMode.
-    // Use a CSS test to determine rendering mode.
-    var elem = $doc.createElement('div');
-    elem.style.cssText = "width:0px;width:1";
-    return parseInt(elem.style.width) != 1 ? $doc.documentElement : $doc.body;
-  }-*/;
-}
diff --git a/user/src/com/google/gwt/user/client/impl/WindowImpl.java b/user/src/com/google/gwt/user/client/impl/WindowImpl.java
index 9bb6333..3be39bc 100644
--- a/user/src/com/google/gwt/user/client/impl/WindowImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/WindowImpl.java
@@ -20,18 +20,6 @@
  * {@link com.google.gwt.user.client.Window}.
  */
 public class WindowImpl {
-  public native void enableScrolling(boolean enable) /*-{
-   @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.style.overflow =
-       enable ? "" : "hidden";
-  }-*/;
-
-  public native int getClientHeight() /*-{
-   return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.clientHeight;
-  }-*/;
-
-  public native int getClientWidth() /*-{
-   return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.clientWidth;
-  }-*/;
 
   public native String getHash() /*-{
     return $wnd.location.hash;
@@ -41,14 +29,6 @@
     return $wnd.location.search;
   }-*/;
   
-  public native int getScrollLeft() /*-{
-   return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft;
-  }-*/;
-
-  public native int getScrollTop() /*-{
-   return @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop;
-  }-*/;
-
   public native void initWindowCloseHandler() /*-{
     var oldOnBeforeUnload = $wnd.onbeforeunload;
     var oldOnUnload = $wnd.onunload;
diff --git a/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java b/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java
deleted file mode 100644
index a6e8d0e..0000000
--- a/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2008 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.impl;
-
-import com.google.gwt.user.client.Element;
-
-/**
- * Opera implementation of {@link com.google.gwt.user.client.impl.WindowImpl}.
- */
-public class WindowImplOpera extends WindowImpl {
-  @SuppressWarnings("unused")
-  private static Element body;
-  
-  /**
-   * In standards mode, on Opera 9.5 (and presumably, above), the clientHeight
-   * and clientWidth are defined on doc.documentElement instead of doc.body.
-   */  
-  @SuppressWarnings("unused")
-  private static native Element getBodyElement() /*-{
-    if (@com.google.gwt.user.client.impl.WindowImplOpera::body == null) {
-      @com.google.gwt.user.client.impl.WindowImplOpera::body =
-        ($doc.compatMode == 'CSS1Compat' && opera.version() >= 9.5) ?
-        $doc.documentElement : $doc.body;
-    }       
-    return @com.google.gwt.user.client.impl.WindowImplOpera::body;
-  }-*/;
-
-  @Override
-  public native int getClientHeight() /*-{
-    return @com.google.gwt.user.client.impl.WindowImplOpera::getBodyElement()().clientHeight;
-  }-*/;  
-  
-  @Override
-  public native int getClientWidth() /*-{
-    return @com.google.gwt.user.client.impl.WindowImplOpera::getBodyElement()().clientWidth;
-  }-*/; 
-}
diff --git a/user/src/com/google/gwt/user/client/impl/WindowImplSafari.java b/user/src/com/google/gwt/user/client/impl/WindowImplSafari.java
deleted file mode 100644
index 75b9130..0000000
--- a/user/src/com/google/gwt/user/client/impl/WindowImplSafari.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2008 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.impl;
-
-/**
- * Safari implementation of {@link com.google.gwt.user.client.impl.WindowImpl}.
- */
-public class WindowImplSafari extends WindowImpl {
-  @Override
-  public native int getClientHeight() /*-{
-    // Safari 2 and Safari 3 disagree on the clientHeight value.
-    // $wnd.devicePixelRatio is only defined in Safari 3.
-    // documentRoot.clientWidth works in both Safari 2 and 3, so we do not need
-    // an override for the width.
-    return $wnd.devicePixelRatio ?
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.clientHeight :
-        $wnd.innerHeight;
-  }-*/;
-
-  @Override
-  public native int getScrollLeft() /*-{
-    return $doc.body.scrollLeft;
-  }-*/;
-
-  @Override
-  public native int getScrollTop() /*-{
-    return $doc.body.scrollTop;
-  }-*/;
-}
diff --git a/user/src/com/google/gwt/user/client/ui/CheckBox.java b/user/src/com/google/gwt/user/client/ui/CheckBox.java
index 09be6ff..4251521 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -20,6 +20,10 @@
 import com.google.gwt.dom.client.LabelElement;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
@@ -55,6 +59,7 @@
   private InputElement inputElem;
   private LabelElement labelElem;
   private boolean valueChangeHandlerInitialized;
+  private boolean valueBeforeClick;
 
   /**
    * Creates a check box with no label.
@@ -111,17 +116,29 @@
 
   public HandlerRegistration addValueChangeHandler(
       ValueChangeHandler<Boolean> handler) {
-    // Is this the first value change handler? If so, time to listen to clicks
-    // on the checkbox
+    // Is this the first value change handler? If so, time to add handlers
     if (!valueChangeHandlerInitialized) {
-      valueChangeHandlerInitialized = true;
-      this.addClickHandler(new ClickHandler() {
-        public void onClick(ClickEvent event) {
-          // No need to compare old value and new value--click handler
-          // only fires on real click, and value always toggles
-          ValueChangeEvent.fire(CheckBox.this, getValue());
+
+      this.addKeyUpHandler(new KeyUpHandler() {
+        public void onKeyUp(KeyUpEvent event) {
+          valueBeforeClick = getValue();
         }
       });
+
+      this.addMouseUpHandler(new MouseUpHandler() {
+        public void onMouseUp(MouseUpEvent event) {
+          valueBeforeClick = getValue();
+        }
+      });
+
+      this.addClickHandler(new ClickHandler() {
+        public void onClick(ClickEvent event) {
+          ValueChangeEvent.fireIfNotEqual(CheckBox.this, valueBeforeClick,
+              getValue());
+        }
+      });
+
+      valueChangeHandlerInitialized = true;
     }
     return addHandler(handler, ValueChangeEvent.getType());
   }
@@ -312,13 +329,15 @@
     }
   }
 
-  // Unlike other widgets the CheckBox sinks on its input element, not its
-  // wrapper element.
+  // Unlike other widgets the CheckBox sinks on its constituent elements, not
+  // their wrapper element.
   @Override
   public void sinkEvents(int eventBitsToAdd) {
     if (isOrWasAttached()) {
       Event.sinkEvents(inputElem, 
           eventBitsToAdd | Event.getEventsSunk(inputElem));
+      Event.sinkEvents(labelElem, 
+          eventBitsToAdd | Event.getEventsSunk(labelElem));
     } else {
       super.sinkEvents(eventBitsToAdd);
     }
diff --git a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
index ca7fa68..1bbdeb6 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
@@ -116,7 +116,7 @@
     if (hiddenDiv == null) {
       hiddenDiv = DOM.createDiv();
       UIObject.setVisible(hiddenDiv, false);
-      DOM.appendChild(RootPanel.getBodyElement(), hiddenDiv);
+      RootPanel.getBodyElement().appendChild(hiddenDiv);
     }
 
     // Hang on to the panel's original parent and sibling elements so that it
@@ -133,6 +133,8 @@
     // Put the panel's element back where it was.
     if (origParent != null) {
       DOM.insertBefore(origParent, getElement(), origSibling);
+    } else {
+      DOM.removeChild(hiddenDiv, getElement());
     }
 
     return child;
diff --git a/user/src/com/google/gwt/user/client/ui/ListBox.java b/user/src/com/google/gwt/user/client/ui/ListBox.java
index 80531fc..35b1b87 100644
--- a/user/src/com/google/gwt/user/client/ui/ListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -312,7 +312,7 @@
    * fail on Internet Explorer 6.0.</em>
    * 
    * @param multiple <code>true</code> to allow multiple selections
-   * @deprecated use {@link #ListBox(boolean) instead
+   * @deprecated use {@link #ListBox(boolean)} instead
    */
   @Deprecated
   public void setMultipleSelect(boolean multiple) {
diff --git a/user/src/com/google/gwt/user/client/ui/RootPanel.java b/user/src/com/google/gwt/user/client/ui/RootPanel.java
index 3f70afb..96999c4 100644
--- a/user/src/com/google/gwt/user/client/ui/RootPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/RootPanel.java
@@ -15,14 +15,15 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.BodyElement;
 import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection;
 import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.Window;
 
 import java.util.HashMap;
@@ -32,7 +33,8 @@
 
 /**
  * The panel to which all other widgets must ultimately be added. RootPanels are
- * never created directly. Rather, they are accessed via {@link RootPanel#get()}.
+ * never created directly. Rather, they are accessed via {@link RootPanel#get()}
+ * .
  * 
  * <p>
  * Most applications will add widgets to the default root panel in their
@@ -67,24 +69,24 @@
   /**
    * Marks a widget as detached and removes it from the detach list.
    * 
+   * <p>
    * If an element belonging to a widget originally passed to
-   * {@link #detachOnWindowClose(Widget)} has been removed from the document, calling
-   * this method will cause it to be marked as detached immediately. Failure to
-   * do so will keep the widget from being garbage collected until the page is
-   * unloaded.
+   * {@link #detachOnWindowClose(Widget)} has been removed from the document,
+   * calling this method will cause it to be marked as detached immediately.
+   * Failure to do so will keep the widget from being garbage collected until
+   * the page is unloaded.
+   * </p>
    * 
+   * <p>
    * This method may only be called per widget, and only for widgets that were
-   * originally passed to {@link #detachOnWindowClose(Widget)}. Any widget in the
-   * detach list, whose element is no longer in the document when the page
-   * unloads, will cause an assertion error.
+   * originally passed to {@link #detachOnWindowClose(Widget)}.
+   * </p>
    * 
    * @param widget the widget that no longer needs to be cleaned up when the
    *          page closes
    * @see #detachOnWindowClose(Widget)
    */
   public static void detachNow(Widget widget) {
-    assert !getBodyElement().isOrHasChild(widget.getElement()) : "detachNow() "
-        + "called on a widget whose element is still attached to the document";
     assert widgetsToDetach.contains(widget) : "detachNow() called on a widget "
         + "not currently in the detach list";
 
@@ -96,12 +98,20 @@
    * Adds a widget to the detach list. This is the list of widgets to be
    * detached when the page unloads.
    * 
+   * <p>
    * This method must be called for all widgets that have no parent widgets.
    * These are most commonly {@link RootPanel RootPanels}, but can also be any
    * widget used to wrap an existing element on the page. Failing to do this may
    * cause these widgets to leak memory. This method is called automatically by
    * widgets' wrap methods (e.g.
    * {@link Button#wrap(com.google.gwt.dom.client.Element)}).
+   * </p>
+   * 
+   * <p>
+   * This method may <em>not</em> be called on any widget whose element is
+   * contained in another widget. This is to ensure that the DOM and Widget
+   * hierarchies cannot get into an inconsistent state.
+   * </p>
    * 
    * @param widget the widget to be cleaned up when the page closes
    * @see #detachNow(Widget)
@@ -109,12 +119,14 @@
   public static void detachOnWindowClose(Widget widget) {
     assert !widgetsToDetach.contains(widget) : "detachOnUnload() called twice "
         + "for the same widget";
+    assert !isElementChildOfWidget(widget.getElement()) : "A widget that has "
+        + "an existing parent widget may not be added to the detach list";
 
     widgetsToDetach.add(widget);
   }
 
   /**
-   * Gets the default root panel. This panel wraps body of the browser's
+   * Gets the default root panel. This panel wraps the body of the browser's
    * document. This root panel can contain any number of widgets, which will be
    * laid out in their natural HTML ordering. Many applications, however, will
    * add a single panel to the RootPanel to provide more structure.
@@ -130,24 +142,34 @@
    * work, the HTML document into which the application is loaded must have
    * specified an element with the given id.
    * 
-   * @param id the id of the element to be wrapped with a root panel
+   * @param id the id of the element to be wrapped with a root panel (
+   *          <code>null</code> specifies the default instance, which wraps the
+   *          &lt;body&gt; element)
    * @return the root panel, or <code>null</code> if no such element was found
    */
   public static RootPanel get(String id) {
     // See if this RootPanel is already created.
     RootPanel rp = rootPanels.get(id);
-    if (rp != null) {
-      return rp;
-    }
 
     // Find the element that this RootPanel will wrap.
     Element elem = null;
     if (id != null) {
-      if (null == (elem = DOM.getElementById(id))) {
+      // Return null if the id is specified, but no element is found.
+      if (null == (elem = Document.get().getElementById(id))) {
         return null;
       }
     }
 
+    if (rp != null) {
+      // If the element associated with an existing RootPanel has been replaced
+      // for any reason, return a new RootPanel rather than the existing one (
+      // see issue 1937).
+      if ((elem == null) || (rp.getElement() == elem)) {
+        // There's already an existing RootPanel for this element. Return it.
+        return rp;
+      }
+    }
+
     // Note that the code in this if block only happens once -
     // on the first RootPanel.get(String) or RootPanel.get()
     // call.
@@ -181,10 +203,20 @@
    * 
    * @return the document's body element
    */
-  public static native Element getBodyElement() /*-{
+  public static native com.google.gwt.user.client.Element getBodyElement() /*-{
     return $doc.body;
   }-*/;
 
+  /**
+   * Determines whether the given widget is in the detach list.
+   * 
+   * @param widget the widget to be checked
+   * @return <code>true</code> if the widget is in the detach list
+   */
+  public static boolean isInDetachList(Widget widget) {
+    return widgetsToDetach.contains(widget);
+  }
+
   // Package-protected for use by unit tests. Do not call this method directly.
   static void detachWidgets() {
     // When the window is closing, detach all widgets that need to be
@@ -194,16 +226,15 @@
       if (widget.isAttached()) {
         widget.onDetach();
       }
-
-      // Assert that each widget's element is actually attached to the
-      // document. If not, then it was probably wrapped and removed, but not
-      // properly detached.
-      assert getBodyElement().isOrHasChild(widget.getElement()) : "A "
-          + "widget in the detach list was found not attached to the "
-          + "document. The is likely caused by wrapping an existing "
-          + "element and removing it from the document without calling "
-          + "RootPanel.detachNow().";
     }
+
+    widgetsToDetach.clear();
+
+    // Clear the RootPanel cache, since we've "detached" all RootPanels at this
+    // point. This would be pointless, since it only happens on unload, but it
+    // is very helpful for unit tests, because it allows RootPanel.get() to work
+    // properly even after a synthesized "unload".
+    rootPanels.clear();
   }
 
   /**
@@ -224,8 +255,29 @@
     });
   }
 
+  /*
+   * Checks to see whether the given element has any parent element that
+   * belongs to a widget. This is not terribly efficient, and is thus only used
+   * in an assertion.
+   */
+  private static boolean isElementChildOfWidget(Element element) {
+    // Walk up the DOM hierarchy, looking for any widget with an event listener
+    // set. Though it is not dependable in the general case that a widget will
+    // have set its element's event listener at all times, it *is* dependable
+    // if the widget is attached. Which it will be in this case.
+    element = element.getParentElement();
+    BodyElement body = Document.get().getBody();
+    while ((element != null) && (body != element)) {
+      if (Event.getEventListener(element) != null) {
+        return true;
+      }
+      element = element.getParentElement().cast();
+    }
+    return false;
+  }
+
   private RootPanel(Element elem) {
-    super(elem);
+    super(elem.<com.google.gwt.user.client.Element> cast());
     onAttach();
   }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
index fcabb5c..7f30b3d 100644
--- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.dom.client.HandlesAllKeyEvents;
 import com.google.gwt.event.dom.client.HasAllKeyHandlers;
 import com.google.gwt.event.dom.client.KeyCodes;
@@ -207,6 +209,7 @@
     }
   }
 
+
   /**
    * Class for menu items in a SuggestionMenu. A SuggestionMenuItem differs from
    * a MenuItem in that each item is backed by a Suggestion object. The text of
@@ -241,6 +244,31 @@
 
   private static final String STYLENAME_DEFAULT = "gwt-SuggestBox";
 
+  /**
+   * Creates a {@link SuggestBox} widget that wraps an existing &lt;input
+   * type='text'&gt; element.
+   * 
+   * This element must already be attached to the document. If the element is
+   * removed from the document, you must call
+   * {@link RootPanel#detachNow(Widget)}.
+   * 
+   * @param oracle the suggest box oracle to use
+   * @param element the element to be wrapped
+   */
+  public static SuggestBox wrap(SuggestOracle oracle, Element element) {
+    // Assert that the element is attached.
+    assert Document.get().getBody().isOrHasChild(element);
+
+    TextBox textBox = new TextBox(element);
+    SuggestBox suggestBox = new SuggestBox(oracle, textBox);
+
+    // Mark it attached and remember it for cleanup.
+    suggestBox.onAttach();
+    RootPanel.detachOnWindowClose(suggestBox);
+
+    return suggestBox;
+  }
+
   private int limit = 20;
   private boolean selectsFirstItem = true;
   private SuggestOracle oracle;
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java
index d6d91c4..9341329 100644
--- a/user/src/com/google/gwt/user/client/ui/UIObject.java
+++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -97,7 +97,7 @@
    * The implementation of the set debug id method, which does nothing by
    * default.
    */
-  public static class DebugIdImpl {
+  private static class DebugIdImpl {
     @SuppressWarnings("unused")
     // parameters
     public void ensureDebugId(UIObject uiObject, String id) {
@@ -113,7 +113,8 @@
    * The implementation of the setDebugId method, which sets the id of the
    * {@link Element}s in this {@link UIObject}.
    */
-  public static class DebugIdImplEnabled extends DebugIdImpl {
+  @SuppressWarnings("unused")
+  private static class DebugIdImplEnabled extends DebugIdImpl {
     @Override
     public void ensureDebugId(UIObject uiObject, String id) {
       uiObject.onEnsureDebugId(id);
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index f3aa9d9..e2e5860 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -89,16 +89,29 @@
     }
     DomEvent.fireNativeEvent(event, this);
   }
- 
+
   /**
-   * Removes this widget from its parent widget. If it has no parent, this
-   * method does nothing.
+   * Removes this widget from its parent widget, if one exists.
+   * 
+   * <p>
+   * If it has no parent, this method does nothing. If it is a "root" widget
+   * (meaning it's been added to the detach list via
+   * {@link RootPanel#detachOnWindowClose(Widget)}), it will be removed from the
+   * detached immediately. This makes it possible for Composites and Panels to
+   * adopt root widgets.
+   * </p>
    * 
    * @throws IllegalStateException if this widget's parent does not support
    *           removal (e.g. {@link Composite})
    */
   public void removeFromParent() {
-    if (parent instanceof HasWidgets) {
+    if (parent == null) {
+      // If the widget had no parent, check to see if it was in the detach list
+      // and remove it if necessary.
+      if (RootPanel.isInDetachList(this)) {
+        RootPanel.detachNow(this);
+      }
+    } else if (parent instanceof HasWidgets) {
       ((HasWidgets) parent).remove(this);
     } else if (parent != null) {
       throw new IllegalStateException(
diff --git a/user/src/com/google/gwt/user/datepicker/client/package-info.java b/user/src/com/google/gwt/user/datepicker/client/package-info.java
new file mode 100644
index 0000000..c6f60c9
--- /dev/null
+++ b/user/src/com/google/gwt/user/datepicker/client/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * The date picker widget and associated types.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.user.datepicker.client;
diff --git a/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc b/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
index f3b8917..bb0d8c3 100644
--- a/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
+++ b/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
@@ -9,10 +9,12 @@
 <html>
   <head>
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
     <!--                                                               -->
     <!-- Consider inlining CSS to reduce the number of requested files -->
     <!--                                                               -->
     <link type="text/css" rel="stylesheet" href="@moduleShortName.css">
+
     <!--                                           -->
     <!-- Any title is fine                         -->
     <!--                                           -->
diff --git a/user/src/com/google/gwt/user/tools/README.txtsrc b/user/src/com/google/gwt/user/tools/README.txtsrc
new file mode 100644
index 0000000..3a9d1c0
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/README.txtsrc
@@ -0,0 +1,62 @@
+--- Generated by GWT WebAppCreator ---
+
+Congratulations, you've successfully generated a starter project!  What next?
+
+-- Option A: Import your project into Eclipse (recommended) --
+
+If you use Eclipse, you can simply import the generated project into Eclipse.
+We've tested against Eclipse 3.3 and 3.4.  Later versions will likely also work,
+eariler versions may not.
+
+In Eclipse, go to the File menu and choose:
+
+  File -> Import... -> Existing Projects into Workspace
+
+  Browse to the directory containing this file,
+  select "@moduleShortName".
+  
+  Be sure to uncheck "Copy projects into workspace" if it is checked.
+  
+  Click Finish.
+  
+You can now browse the project in Eclipse.
+
+To launch your web app in GWT hosted mode, go to the Run menu and choose:
+
+  Run -> Open Debug Dialog...
+
+  Under Java Application, you should find a launch configuration
+  named "@moduleShortName".  Select and click "Debug".
+
+  You can now use the built-in debugger to debug your web app in hosted mode.
+
+To compile for web mode, just run your app in hosted mode and press the
+"Compile/Browse" button.
+
+-- Option B: Build from the command line with Ant --
+
+If you prefer to work from the command line, you can use Ant to build your
+project. (http://ant.apache.org/)  Ant uses the generated 'build.xml' file
+which describes exactly how to build your project.  This file has been tested
+to work against Ant 1.7.1.  The following assumes 'ant' is on your command
+line path.
+
+To run hosted mode, just type 'ant hosted'.
+
+To compile your project for deployment, just type 'ant'.
+
+To compile and also bundle into a .war file, type 'ant war'.
+
+For a full listing of other targets, type 'ant -p'.
+
+-- Option C: Using another IDE --
+
+GWT projects can be run in other IDEs as well, but will require some manual
+setup.  If you go this route, be sure to:
+
+* Have your IDE build .class files into 'war/WEB-INF/classes'.
+* Add gwt-user.jar and gwt-dev-<platform>.jar to your project build path.
+* When creating a launch configuration, add a classpath entry for your 'src'
+  folder (this is somewhat unusual but GWT needs access to your source files).
+
+If you get stuck, try to mimic what the Ant 'build.xml' would do.
diff --git a/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc b/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
index 2b122de..4278c86 100644
--- a/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
@@ -4,8 +4,9 @@
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 /**
- * The server side implementation of the Rpc service.
+ * The server side implementation of the RPC service.
  */
+@SuppressWarnings("serial")
 public class GreetingServiceImpl extends RemoteServiceServlet implements
     GreetingService {
 
diff --git a/user/src/com/google/gwt/user/tools/WebAppCreator.java b/user/src/com/google/gwt/user/tools/WebAppCreator.java
index 9dd36be..280372e 100644
--- a/user/src/com/google/gwt/user/tools/WebAppCreator.java
+++ b/user/src/com/google/gwt/user/tools/WebAppCreator.java
@@ -101,7 +101,7 @@
 
     @Override
     public String getPurpose() {
-      return "The name of the module to create (fully-qualified Java class name)";
+      return "The name of the module to create (e.g. com.example.myapp.MyApp)";
     }
 
     @Override
@@ -302,6 +302,7 @@
       files.add(new FileCreator(serverDir, "GreetingServiceImpl" + ".java",
           "RpcServerTemplate.java"));
       files.add(new FileCreator(outDir, "build.xml", "project.ant.xml"));
+      files.add(new FileCreator(outDir, "README.txt", "README.txt"));
     }
     if (!noEclipse) {
       assert new File(gwtDevPath).isAbsolute();
@@ -314,14 +315,13 @@
 
     // copy source files, replacing the content as needed
     for (FileCreator fileCreator : files) {
+      URL url = WebAppCreator.class.getResource(fileCreator.sourceName + "src");
+      if (url == null) {
+        throw new FileNotFoundException(fileCreator.sourceName + "src");
+      }
       File file = Utility.createNormalFile(fileCreator.destDir,
           fileCreator.destName, overwrite, ignore);
       if (file != null) {
-        URL url = WebAppCreator.class.getResource(fileCreator.sourceName
-            + "src");
-        if (url == null) {
-          throw new FileNotFoundException(fileCreator.sourceName + "src");
-        }
         String data = Util.readURLAsString(url);
         Utility.writeTemplateFile(file, data, replacements);
       }
@@ -329,10 +329,10 @@
 
     // copy libs directly
     for (FileCreator fileCreator : libs) {
+      FileInputStream is = new FileInputStream(fileCreator.sourceName);
       File file = Utility.createNormalFile(fileCreator.destDir,
           fileCreator.destName, overwrite, ignore);
       if (file != null) {
-        FileInputStream is = new FileInputStream(fileCreator.sourceName);
         FileOutputStream os = new FileOutputStream(file);
         Util.copy(is, os);
       }
diff --git a/user/test/com/google/gwt/debug/client/DebugInfoTest.java b/user/test/com/google/gwt/debug/client/DebugInfoTest.java
index 04fcaeb..9d7f4f4 100644
--- a/user/test/com/google/gwt/debug/client/DebugInfoTest.java
+++ b/user/test/com/google/gwt/debug/client/DebugInfoTest.java
@@ -18,7 +18,7 @@
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
- * Test Case for {@link DebugInfo}.
+ * Test Case for {@link DebugInfo} when <code>gwt.enableDebugId</code> is enabled.
  */
 public class DebugInfoTest extends GWTTestCase {
 
diff --git a/user/test/com/google/gwt/debug/client/DebugInfoTestDisabled.java b/user/test/com/google/gwt/debug/client/DebugInfoTestDisabled.java
new file mode 100644
index 0000000..c3677be
--- /dev/null
+++ b/user/test/com/google/gwt/debug/client/DebugInfoTestDisabled.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 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.debug.client;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Test Case for {@link DebugInfo} when <code>gwt.enableDebugId</code> is disabled.
+ */
+public class DebugInfoTestDisabled extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.DebugTestDisabled";
+  }
+
+  /**
+   * Test that the {@link DebugInfo#isDebugIdEnabled()} method works correctly when debug ids are disabled.
+   */
+  public void testIsDebugIdDisabled() {
+    assertFalse(DebugInfo.isDebugIdEnabled());
+  }
+}
diff --git a/user/test/com/google/gwt/dom/client/ElementTest.java b/user/test/com/google/gwt/dom/client/ElementTest.java
index f51fe92..a4f965a 100644
--- a/user/test/com/google/gwt/dom/client/ElementTest.java
+++ b/user/test/com/google/gwt/dom/client/ElementTest.java
@@ -31,7 +31,7 @@
   }
 
   /**
-   * [get|set|remove]Attribute
+   * [get|set|remove]Attribute.
    */
   public void testElementAttribute() {
     DivElement div = Document.get().createDivElement();
@@ -44,7 +44,21 @@
   }
 
   /**
-   * getAbsolute[Left|Top]
+   * Ensure that the return type of an attribute is always a string. IE should
+   * not return a numeric attribute based on the element property. See issue
+   * 3238.
+   */
+  public void testElementAttributeNumeric() {
+    DivElement div = Document.get().createDivElement();
+    Document.get().getBody().appendChild(div);
+    div.setInnerText("Hello World");
+    div.getAttribute("offsetWidth").length();
+    div.getAttribute("offsetWidth").trim().length();
+    Document.get().getBody().removeChild(div);
+  }
+
+  /**
+   * getAbsolute[Left|Top].
    */
   public void testGetAbsolutePosition() {
     final int border = 8;
@@ -77,7 +91,7 @@
   }
 
   /**
-   * scroll[Left|Top], scrollIntoView
+   * scroll[Left|Top], scrollIntoView.
    */
   public void testGetAbsolutePositionWhenScrolled() {
     final DivElement outer = Document.get().createDivElement();
@@ -111,7 +125,7 @@
   }
 
   /**
-   * getParentElement
+   * getParentElement.
    */
   public void testGetParent() {
     Element element = Document.get().getBody();
@@ -131,7 +145,7 @@
   }
 
   /**
-   * firstChildElement, nextSiblingElement
+   * firstChildElement, nextSiblingElement.
    */
   public void testChildElements() {
     Document doc = Document.get();
@@ -151,7 +165,7 @@
   }
 
   /**
-   * isOrHasChild
+   * isOrHasChild.
    */
   public void testIsOrHasChild() {
     DivElement div = Document.get().createDivElement();
@@ -171,7 +185,7 @@
   }
 
   /**
-   * innerText
+   * innerText.
    */
   public void testSetInnerText() {
     Document doc = Document.get();
@@ -198,7 +212,7 @@
   }
 
   /**
-   * innerHTML
+   * innerHTML.
    */
   public void testSetInnerHTML() {
     DivElement div = Document.get().createDivElement();
@@ -213,7 +227,7 @@
   }
 
   /**
-   * setProperty*, getProperty*
+   * setProperty*, getProperty*.
    */
   public void testProperties() {
     DivElement div = Document.get().createDivElement();
@@ -231,7 +245,7 @@
   }
 
   /**
-   * className, id, tagName, title, dir, lang
+   * className, id, tagName, title, dir, lang.
    */
   public void testNativeProperties() {
     DivElement div = Document.get().createDivElement();
@@ -255,7 +269,7 @@
   }
 
   /**
-   * style
+   * style.
    */
   public void testStyle() {
     DivElement div = Document.get().createDivElement();
@@ -303,7 +317,7 @@
   }
 
   /**
-   * offset[Left|Top|Width|Height], offsetParent
+   * offset[Left|Top|Width|Height], offsetParent.
    */
   public void testOffsets() {
     DivElement outer = Document.get().createDivElement();
@@ -329,7 +343,7 @@
   }
 
   /**
-   * getElementsByTagName
+   * getElementsByTagName.
    */
   public void testGetElementsByTagName() {
     DivElement div = Document.get().createDivElement();
diff --git a/user/test/com/google/gwt/user/DebugTestDisabled.gwt.xml b/user/test/com/google/gwt/user/DebugTestDisabled.gwt.xml
new file mode 100644
index 0000000..0b0e425
--- /dev/null
+++ b/user/test/com/google/gwt/user/DebugTestDisabled.gwt.xml
@@ -0,0 +1,19 @@
+<!--                                                                        -->
+<!-- Copyright 2008 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<module>
+  <inherits name="com.google.gwt.user.User"/>
+  <inherits name="com.google.gwt.user.Debug"/>
+  <set-property name="gwt.enableDebugId" value="false"/>
+</module>
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index c5bac76..0566294 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -23,7 +23,6 @@
 import com.google.gwt.user.client.WindowTest;
 import com.google.gwt.user.client.ui.AbsolutePanelTest;
 import com.google.gwt.user.client.ui.AnchorTest;
-import com.google.gwt.user.client.ui.WidgetTest;
 import com.google.gwt.user.client.ui.ButtonTest;
 import com.google.gwt.user.client.ui.CaptionPanelTest;
 import com.google.gwt.user.client.ui.CheckBoxTest;
@@ -66,6 +65,7 @@
 import com.google.gwt.user.client.ui.PrefixTreeTest;
 import com.google.gwt.user.client.ui.RadioButtonTest;
 import com.google.gwt.user.client.ui.RichTextAreaTest;
+import com.google.gwt.user.client.ui.RootPanelTest;
 import com.google.gwt.user.client.ui.ScrollPanelTest;
 import com.google.gwt.user.client.ui.SimpleCheckBoxTest;
 import com.google.gwt.user.client.ui.SimpleRadioButtonTest;
@@ -82,6 +82,7 @@
 import com.google.gwt.user.client.ui.WidgetIteratorsTest;
 import com.google.gwt.user.client.ui.WidgetOnLoadTest;
 import com.google.gwt.user.client.ui.WidgetSubclassingTest;
+import com.google.gwt.user.client.ui.WidgetTest;
 import com.google.gwt.user.client.ui.impl.ClippedImagePrototypeTest;
 import com.google.gwt.user.datepicker.client.DateChangeEventTest;
 import com.google.gwt.user.rebind.ui.ImageBundleGeneratorTest;
@@ -168,6 +169,7 @@
     suite.addTestSuite(DateChangeEventTest.class);
     suite.addTestSuite(CreateEventTest.class);
     suite.addTestSuite(WidgetTest.class);
+    suite.addTestSuite(RootPanelTest.class);
     return suite;
   }
 }
diff --git a/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java
index ff407d0..d1a7de7 100644
--- a/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java
+++ b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java
@@ -32,8 +32,21 @@
   @SuppressWarnings("unused")
   private Date dateTypeExposeToSerialization;
 
-  private final Date endDate;
-  private final Date startDate;
+  /**
+   * Transient to avoid 'cannot serialize final field' RPC warning.
+   * 
+   * FIXME: when final fields are supported, or don't generate warnings for
+   * custom serialized classes.
+   */
+  private final transient Date endDate;
+
+  /**
+   * Transient to avoid 'cannot serialize final field' RPC warning.
+   * 
+   * FIXME: when final fields are supported, or don't generate warnings for
+   * custom serialized classes.
+   */
+  private final transient Date startDate;
 
   public ManuallySerializedImmutableClass(Date startDate, Date endDate) {
     this.startDate = startDate;
diff --git a/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
index 1a8d5f0..27a8b50 100644
--- a/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
@@ -34,6 +34,9 @@
     return "com.google.gwt.user.UserTest";
   }
 
+  /**
+   * Tests {@link Anchor#wrap(Element)}.
+   */
   public void testAnchor() {
     ensureDiv().setInnerHTML("<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
     Anchor anchor = Anchor.wrap(Document.get().getElementById("foo"));
@@ -43,6 +46,9 @@
     assertEquals("myAnchor", anchor.getText());
   }
 
+  /**
+   * Tests {@link Button#wrap(Element)}.
+   */
   public void testButton() {
     ensureDiv().setInnerHTML("<button id='foo'>myButton</button>");
     Button button = Button.wrap(Document.get().getElementById("foo"));
@@ -51,136 +57,10 @@
     assertEquals("myButton", button.getText());
   }
 
-  public void testFileUpload() {
-    ensureDiv().setInnerHTML("<input type='file' id='foo'>myInput</input>");
-    FileUpload upload = FileUpload.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(upload);
-  }
-
-  public void testFormPanel() {
-    ensureDiv().setInnerHTML("<form id='foo'></form>");
-    FormPanel formPanel = FormPanel.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(formPanel);
-  }
-
-  public void testFrame() {
-    ensureDiv().setInnerHTML("<iframe id='foo'>myFrame</iframe>");
-    Frame frame = Frame.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(frame);
-  }
-
-  public void testHidden() {
-    ensureDiv().setInnerHTML("<input type='hidden' id='foo'></input>");
-    Hidden hidden = Hidden.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(hidden);
-  }
-
-  public void testHTML() {
-    ensureDiv().setInnerHTML("<div id='foo'>myHTML</div>");
-    HTML html = HTML.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(html);
-    assertEquals("myHTML", html.getHTML());
-  }
-
-  public void testImage() {
-    ensureDiv().setInnerHTML("<img id='foo' src='" + IMG_URL + "'>");
-    Image image = Image.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(image);
-    assertEquals(IMG_URL, image.getUrl());
-  }
-
-  public void testInlineHTML() {
-    ensureDiv().setInnerHTML("<span id='foo'>myInlineHTML</span>");
-    InlineHTML html = InlineHTML.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(html);
-    assertEquals("myInlineHTML", html.getHTML());
-  }
-
-  public void testInlineLabel() {
-    ensureDiv().setInnerHTML("<span id='foo'>myInlineLabel</span>");
-    InlineLabel label = InlineLabel.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(label);
-    assertEquals("myInlineLabel", label.getText());
-  }
-
-  public void testLabel() {
-    ensureDiv().setInnerHTML("<div id='foo'>myLabel</div>");
-    Label label = Label.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(label);
-    assertEquals("myLabel", label.getText());
-  }
-
-  public void testListBox() {
-    ensureDiv().setInnerHTML("<select id='foo'></select>");
-    ListBox listBox = ListBox.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(listBox);
-  }
-
-  public void testPasswordTextBox() {
-    ensureDiv().setInnerHTML("<input type='password' id='foo'></input>");
-    PasswordTextBox textBox = PasswordTextBox.wrap(Document.get().getElementById(
-        "foo"));
-
-    assertExistsAndAttached(textBox);
-  }
-
-  public void testSimpleCheckBox() {
-    ensureDiv().setInnerHTML("<input type='checkbox' id='foo'></input>");
-    SimpleCheckBox checkBox = SimpleCheckBox.wrap(Document.get().getElementById(
-        "foo"));
-
-    assertExistsAndAttached(checkBox);
-  }
-
-  public void testSimpleRadioButton() {
-    ensureDiv().setInnerHTML("<input type='radio' id='foo'></input>");
-    SimpleRadioButton radio = SimpleRadioButton.wrap(Document.get().getElementById(
-        "foo"));
-
-    assertExistsAndAttached(radio);
-  }
-
-  public void testTextArea() {
-    ensureDiv().setInnerHTML("<textarea rows='1' cols='1' id='foo'></textarea>");
-    TextArea textArea = TextArea.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(textArea);
-  }
-
-  public void testTextBox() {
-    ensureDiv().setInnerHTML("<input type='text' id='foo'></input>");
-    TextBox textBox = TextBox.wrap(Document.get().getElementById("foo"));
-
-    assertExistsAndAttached(textBox);
-  }
-
-  public void testDetachOnUnloadTwiceFails() {
-    // Testing hosted-mode-only assertion.
-    if (!GWT.isScript()) {
-      try {
-        // Trying to pass the same widget to RootPanel.detachOnUnload() twice
-        // should fail an assertion (the first call is implicit through
-        // Anchor.wrap()).
-        ensureDiv().setInnerHTML(
-            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
-        Anchor a = Anchor.wrap(Document.get().getElementById("foo")); // pass
-        RootPanel.detachOnWindowClose(a); // fail
-        fail("Expected assertion failure calling detachOnLoad() twice");
-      } catch (AssertionError e) {
-      }
-    }
-  }
-
+  /**
+   * Tests that {@link RootPanel#detachNow(Widget)} can only be called once per
+   * widget.
+   */
   public void testDetachNowTwiceFails() {
     // Testing hosted-mode-only assertion.
     if (!GWT.isScript()) {
@@ -198,6 +78,245 @@
     }
   }
 
+  /**
+   * Tests that {@link RootPanel#detachOnWindowClose(Widget)} can only be called
+   * once per widget.
+   */
+  public void testDetachOnWindowCloseTwiceFails() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      try {
+        // Trying to pass the same widget to RootPanel.detachOnUnload() twice
+        // should fail an assertion (the first call is implicit through
+        // Anchor.wrap()).
+        ensureDiv().setInnerHTML(
+            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
+        Anchor a = Anchor.wrap(Document.get().getElementById("foo")); // pass
+        RootPanel.detachOnWindowClose(a); // fail
+        fail("Expected assertion failure calling detachOnLoad() twice");
+      } catch (AssertionError e) {
+      }
+    }
+  }
+
+  /**
+   * Tests {@link FileUpload#wrap(Element)}.
+   */
+  public void testFileUpload() {
+    ensureDiv().setInnerHTML("<input type='file' id='foo'>myInput</input>");
+    FileUpload upload = FileUpload.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(upload);
+  }
+
+  /**
+   * Tests {@link FormPanel#wrap(Element)}.
+   */
+  public void testFormPanel() {
+    ensureDiv().setInnerHTML("<form id='foo'></form>");
+    FormPanel formPanel = FormPanel.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(formPanel);
+  }
+
+  /**
+   * Tests {@link Frame#wrap(Element)}.
+   */
+  public void testFrame() {
+    ensureDiv().setInnerHTML("<iframe id='foo'>myFrame</iframe>");
+    Frame frame = Frame.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(frame);
+  }
+
+  /**
+   * Tests {@link Hidden#wrap(Element)}.
+   */
+  public void testHidden() {
+    ensureDiv().setInnerHTML("<input type='hidden' id='foo'></input>");
+    Hidden hidden = Hidden.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(hidden);
+  }
+
+  /**
+   * Tests {@link HTML#wrap(Element)}.
+   */
+  public void testHTML() {
+    ensureDiv().setInnerHTML("<div id='foo'>myHTML</div>");
+    HTML html = HTML.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(html);
+    assertEquals("myHTML", html.getHTML());
+  }
+
+  /**
+   * Tests {@link Image#wrap(Element)}.
+   */
+  public void testImage() {
+    ensureDiv().setInnerHTML("<img id='foo' src='" + IMG_URL + "'>");
+    Image image = Image.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(image);
+    assertEquals(IMG_URL, image.getUrl());
+  }
+
+  /**
+   * Tests {@link InlineHTML#wrap(Element)}.
+   */
+  public void testInlineHTML() {
+    ensureDiv().setInnerHTML("<span id='foo'>myInlineHTML</span>");
+    InlineHTML html = InlineHTML.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(html);
+    assertEquals("myInlineHTML", html.getHTML());
+  }
+
+  /**
+   * Tests {@link InlineLabel#wrap(Element)}.
+   */
+  public void testInlineLabel() {
+    ensureDiv().setInnerHTML("<span id='foo'>myInlineLabel</span>");
+    InlineLabel label = InlineLabel.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(label);
+    assertEquals("myInlineLabel", label.getText());
+  }
+
+  /**
+   * Tests {@link Label#wrap(Element)}.
+   */
+  public void testLabel() {
+    ensureDiv().setInnerHTML("<div id='foo'>myLabel</div>");
+    Label label = Label.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(label);
+    assertEquals("myLabel", label.getText());
+  }
+
+  /**
+   * Tests {@link ListBox#wrap(Element)}.
+   */
+  public void testListBox() {
+    ensureDiv().setInnerHTML("<select id='foo'></select>");
+    ListBox listBox = ListBox.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(listBox);
+  }
+
+  /**
+   * Tests that all widgets passed to
+   * {@link RootPanel#detachOnWindowClose(Widget)} are cleaned up properly when
+   * the window is unloaded, regardless of whether their associated elements are
+   * still in the DOM or not.
+   */
+  public void testOnUnloadDetachesAllWidgets() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      ensureDiv().setInnerHTML(
+          "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>"
+              + "<a id='bar' href='" + TEST_URL + "'>myOtherAnchor</a>");
+
+      // Wrap one widget that will be left in the DOM normally.
+      Element fooElem = Document.get().getElementById("foo");
+      Anchor fooAnchor = Anchor.wrap(fooElem);
+
+      // Wrap another widget and remove its element from the DOM.
+      Element barElem = Document.get().getElementById("bar");
+      Anchor barAnchor = Anchor.wrap(barElem);
+      barElem.getParentElement().removeChild(barElem);
+
+      // Fake an unload by telling the RootPanel to go ahead and detach all
+      // of its widgets.
+      RootPanel.detachWidgets();
+
+      // Now make sure that both widgets were detached properly.
+      assertFalse("fooAnchor should have been detached", fooAnchor.isAttached());
+      assertFalse("barAnchor should have been detached", barAnchor.isAttached());
+    }
+  }
+
+  /**
+   * Tests {@link PasswordTextBox#wrap(Element)}.
+   */
+  public void testPasswordTextBox() {
+    ensureDiv().setInnerHTML("<input type='password' id='foo'></input>");
+    PasswordTextBox textBox = PasswordTextBox.wrap(Document.get().getElementById(
+        "foo"));
+
+    assertExistsAndAttached(textBox);
+  }
+
+  /**
+   * Tests {@link SimpleCheckBox#wrap(Element)}.
+   */
+  public void testSimpleCheckBox() {
+    ensureDiv().setInnerHTML("<input type='checkbox' id='foo'></input>");
+    SimpleCheckBox checkBox = SimpleCheckBox.wrap(Document.get().getElementById(
+        "foo"));
+
+    assertExistsAndAttached(checkBox);
+  }
+
+  /**
+   * Tests {@link SimpleRadioButton#wrap(Element)}.
+   */
+  public void testSimpleRadioButton() {
+    ensureDiv().setInnerHTML("<input type='radio' id='foo'></input>");
+    SimpleRadioButton radio = SimpleRadioButton.wrap(Document.get().getElementById(
+        "foo"));
+
+    assertExistsAndAttached(radio);
+  }
+
+  /**
+   * Tests {@link TextArea#wrap(Element)}.
+   */
+  public void testTextArea() {
+    ensureDiv().setInnerHTML("<textarea rows='1' cols='1' id='foo'></textarea>");
+    TextArea textArea = TextArea.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(textArea);
+  }
+
+  /**
+   * Tests {@link TextBox#wrap(Element)}.
+   */
+  public void testTextBox() {
+    ensureDiv().setInnerHTML("<input type='text' id='foo'></input>");
+    TextBox textBox = TextBox.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(textBox);
+  }
+
+  /**
+   * Tests that wrapping an element that is already a child of an existing
+   * widget's element fails.
+   */
+  public void testWrappingChildElementFails() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      try {
+        // Create a panel that contains HTML with a unique id, which we're
+        // going to try and wrap below.
+        FlowPanel p = new FlowPanel();
+        RootPanel.get().add(p);
+        p.add(new HTML("<a id='twcef_id'>foo</a>"));
+
+        // Get the element and try to wrap it.
+        Element unwrappableElement = Document.get().getElementById("twcef_id");
+        Anchor.wrap(unwrappableElement);
+        fail("Attempting to wrap the above element should have failed.");
+      } catch (AssertionError e) {
+        // Expected error.
+      }
+    }
+  }
+
+  /**
+   * Tests that wrap() may only be called on elements that are already attached
+   * to the DOM.
+   */
   public void testWrapUnattachedFails() {
     // Testing hosted-mode-only assertion.
     if (!GWT.isScript()) {
@@ -214,28 +333,6 @@
     }
   }
 
-  public void testOnUnloadAssertions() {
-    // Testing hosted-mode-only assertion.
-    if (!GWT.isScript()) {
-      try {
-        // When a wrap()ed element is detached from the document without being
-        // properly unwrapped, there will be an assertion to catch this run on
-        // unload.
-        ensureDiv().setInnerHTML(
-            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
-        Element aElem = Document.get().getElementById("foo");
-        Anchor.wrap(aElem);
-        aElem.getParentElement().removeChild(aElem);
-
-        // Fake an unload by telling the RootPanel to go ahead and detach all
-        // of its widgets.
-        RootPanel.detachWidgets();
-        fail("Assertion expected for orphaned wrap()ed widgets");
-      } catch (AssertionError e) {
-      }
-    }
-  }
-
   private void assertExistsAndAttached(Widget widget) {
     assertNotNull(widget);
     assertTrue(widget.isAttached());
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
index 8d6986d..9e7f8ac 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
@@ -15,10 +15,9 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.Node;
 import com.google.gwt.junit.client.GWTTestCase;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
 
 /**
  * Tests the HTMLPanel widget.
@@ -44,10 +43,8 @@
     p.add(labelA, "a");
     p.add(labelB, "b");
     // Ensure that both Label's have the correct parent.
-    assertEquals("a", DOM.getElementAttribute(
-        DOM.getParent(labelA.getElement()), "id"));
-    assertEquals("b", DOM.getElementAttribute(
-        DOM.getParent(labelB.getElement()), "id"));
+    assertEquals("a", labelA.getElement().getParentElement().getId());
+    assertEquals("b", labelB.getElement().getParentElement().getId());
   }
 
   /**
@@ -70,9 +67,12 @@
 
     // If all goes well, the HTMLPanel's element should still be properly
     // connected to the SimplePanel's element.
-    assertTrue(DOM.isOrHasChild(sp.getElement(), p.getElement()));
+    assertTrue(sp.getElement().isOrHasChild(p.getElement()));
   }
 
+  /**
+   * Tests child attachment order using {@link HasWidgetsTester}.
+   */
   public void testAttachDetachOrder() {
     HTMLPanel p = new HTMLPanel("<div id='w00t'></div>");
     HasWidgetsTester.testAll(p, new Adder());
@@ -96,9 +96,9 @@
 
     hp.add(new Button("foo"), "foo");
 
-    assertTrue(DOM.isOrHasChild(fp.getElement(), hp.getElement()));
-    assertTrue(DOM.getNextSibling(ba.getElement()) == hp.getElement());
-    assertTrue(DOM.getNextSibling(hp.getElement()) == bb.getElement());
+    assertTrue(fp.getElement().isOrHasChild(hp.getElement()));
+    assertTrue(ba.getElement().getNextSibling() == hp.getElement());
+    assertTrue(hp.getElement().getNextSibling() == bb.getElement());
   }
 
   /**
@@ -119,6 +119,10 @@
     assertEquals("bar", next.getNodeValue());
   }
 
+  /**
+   * Ensure that {@link HTMLPanel#getElementById(String)} behaves properly in
+   * both attached and unattached states.
+   */
   public void testGetElementById() {
     HTMLPanel hp = new HTMLPanel("foo<div id='toFind'>bar</div>baz");
 
@@ -130,4 +134,27 @@
     // Make sure we got the same element in both cases.
     assertEquals(elem0, elem1);
   }
+
+  /**
+   * Tests that the HTMLPanel's element is not moved from its original location
+   * when {@link HTMLPanel#add(Widget, String)} is called on it while it is
+   * unattached.
+   */
+  public void testElementIsUnmoved() {
+    HTMLPanel unattached = new HTMLPanel("<div id='unattached'></div>");
+    HTMLPanel attached = new HTMLPanel("<div id='attached'></div>");
+
+    RootPanel.get().add(attached);
+
+    Element unattachedParentElem = unattached.getElement().getParentElement();
+    Element attachedParentElem = attached.getElement().getParentElement();
+
+    unattached.add(new Button("unattached"), "unattached");
+    attached.add(new Button("attached"), "attached");
+
+    assertEquals("Unattached's parent element should be unaffected",
+        unattachedParentElem, unattached.getElement().getParentElement());
+    assertEquals("Unattached's parent element should be unaffected",
+        attachedParentElem, attached.getElement().getParentElement());
+  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/ImageTest.java b/user/test/com/google/gwt/user/client/ui/ImageTest.java
index 2201dd2..f4b222e 100644
--- a/user/test/com/google/gwt/user/client/ui/ImageTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ImageTest.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.DivElement;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.event.dom.client.ErrorEvent;
 import com.google.gwt.event.dom.client.ErrorHandler;
@@ -26,6 +27,7 @@
  * Tests for the Image widget. Images in both clipped mode and unclipped mode
  * are tested, along with the transitions between the two modes.
  */
+@SuppressWarnings("deprecation")
 public class ImageTest extends GWTTestCase {
   private static class TestErrorHandler implements ErrorHandler {
     private Image image;
@@ -41,17 +43,13 @@
 
   @Deprecated
   private abstract static class TestLoadListener implements LoadListener {
-    private Image image;
     private boolean finished = false;
+    private Image image;
 
     public TestLoadListener(Image image) {
       this.image = image;
     }
 
-    public void onError(Widget sender) {
-      fail("The image " + image.getUrl() + " failed to load.");
-    }
-
     /**
      * Mark the test as finished.
      */
@@ -65,32 +63,72 @@
     public boolean isFinished() {
       return finished;
     }
+
+    public void onError(Widget sender) {
+      fail("The image " + image.getUrl() + " failed to load.");
+    }
   }
-  
+
   /**
    * Helper method that allows us to 'peek' at the private <code>state</code>
    * field in the Image object, and call the <code>state.getStateName()</code>
    * method.
-   *
+   * 
    * @param image The image instance
-   * @return "unclipped" if image is in the unclipped state, or
-   *         "clipped" if the image is in the clipped state
+   * @return "unclipped" if image is in the unclipped state, or "clipped" if the
+   *         image is in the clipped state
    */
   public static native String getCurrentImageStateName(Image image) /*-{
     var imgState = image.@com.google.gwt.user.client.ui.Image::state;
     return imgState.@com.google.gwt.user.client.ui.Image.State::getStateName() ();
   }-*/;
 
-  @Override
-  public String getModuleName() {
-    return "com.google.gwt.user.UserTest";
+  private int firedError;
+
+  private int firedLoad;
+
+  /**
+   * Tests the transition from the clipped state to the unclipped state.
+   * 
+   * Disabled because of issue #863 & #864. It fails intermittently in linux
+   * hosted mode tests.
+   */
+  public void disabledTestChangeClippedImageToUnclipped() {
+    final Image image = new Image("counting-forwards.png", 12, 13, 8, 8);
+    assertEquals("clipped", getCurrentImageStateName(image));
+
+    image.addLoadListener(new LoadListener() {
+      private int onLoadEventCount = 0;
+
+      public void onError(Widget sender) {
+        fail("The image " + ((Image) sender).getUrl() + " failed to load.");
+      }
+
+      public void onLoad(Widget sender) {
+        ++onLoadEventCount;
+        if (onLoadEventCount == 1) { // Set the url after the first image loads
+          image.setUrl("counting-forwards.png");
+        } else if (onLoadEventCount == 2) {
+          assertEquals(0, image.getOriginLeft());
+          assertEquals(0, image.getOriginTop());
+          assertEquals(32, image.getWidth());
+          assertEquals(32, image.getHeight());
+          assertEquals("unclipped", getCurrentImageStateName(image));
+          finishTest();
+        }
+      }
+    });
+
+    delayTestFinish(5000);
+    RootPanel.get().add(image);
   }
 
   /**
-   * Tests the transition from the unclipped state to the clipped state
+   * Tests the transition from the unclipped state to the clipped state.
+   * 
+   * Disabled because of issue #863.
    */
-  /* This test is commented out because of issue #863
-  public void testChangeImageToClipped() {
+  public void disabledTestChangeImageToClipped() {
     final Image image = new Image("counting-forwards.png");
     assertEquals("unclipped", getCurrentImageStateName(image));
 
@@ -117,56 +155,16 @@
       }
     });
 
-    RootPanel.get().add(image);
-
     delayTestFinish(5000);
+    RootPanel.get().add(image);
   }
-  */
 
   /**
-   * Tests the transition from the clipped state to the unclipped state.
+   * Tests the creation of an image in unclipped mode.
+   * 
+   * Disabled because of issue #863 & #864.
    */
-  /* This test is commented out because of issue #863 & #864
-     It fails intermittently in linux hosted mode tests.
-  public void testChangeClippedImageToUnclipped() {
-    final Image image = new Image("counting-forwards.png",
-        12, 13, 8, 8);
-    assertEquals("clipped", getCurrentImageStateName(image));
-
-    image.addLoadListener(new LoadListener() {
-      private int onLoadEventCount = 0;
-
-      public void onError(Widget sender) {
-        fail("The image " + ((Image) sender).getUrl() + " failed to load.");
-      }
-
-      public void onLoad(Widget sender) {
-        ++onLoadEventCount;
-        if (onLoadEventCount == 1) {
-          // Set the url after the first image loads
-          image.setUrl("counting-forwards.png");
-        } else if (onLoadEventCount == 2) {
-          assertEquals(0, image.getOriginLeft());
-          assertEquals(0, image.getOriginTop());
-          assertEquals(32, image.getWidth());
-          assertEquals(32, image.getHeight());
-          assertEquals("unclipped", getCurrentImageStateName(image));
-          finishTest();
-        }
-      }
-    });
-
-    RootPanel.get().add(image);
-
-    delayTestFinish(5000);
-  }
-  */
-  
-  /**
-   *  Tests the creation of an image in unclipped mode
-   */
-  /* This test is commented out because of issue #864 and issue #863
-  public void testCreateImage() {
+  public void disabledTestCreateImage() {
     final Image image = new Image("counting-forwards.png");
 
     image.addLoadListener(new LoadListener() {
@@ -185,67 +183,21 @@
       }
     });
 
+    delayTestFinish(5000);
     RootPanel.get().add(image);
     assertEquals(0, image.getOriginLeft());
     assertEquals(0, image.getOriginTop());
     assertEquals("unclipped", getCurrentImageStateName(image));
-
-    delayTestFinish(5000);
-  }
-  */
-
-  /**
-   * Tests the creation of an image in clipped mode.
-   */
-  public void testCreateClippedImage() {
-    final Image image = new Image("counting-forwards.png",
-        16, 16, 16, 16);
-
-    final TestLoadListener listener = new TestLoadListener(image) {
-      private int onLoadEventCount = 0;
-
-      public void onLoad(Widget sender) {
-        if (++onLoadEventCount == 1) {
-          assertEquals(16, image.getWidth());
-          assertEquals(16, image.getHeight());
-          finish();
-        }
-      }
-    };
-    image.addLoadListener(listener);
-
-    image.addLoadHandler(new LoadHandler() {
-      private int onLoadEventCount = 0;
-
-      public void onLoad(LoadEvent event) {
-        if (++onLoadEventCount == 1) {
-          assertEquals(16, image.getWidth());
-          assertEquals(16, image.getHeight());
-          if (listener.isFinished()) {
-            finishTest();
-          } else {
-            fail("Listener did not fire first");
-          }
-        }
-      }
-    });
-    image.addErrorHandler(new TestErrorHandler(image));
-
-    RootPanel.get().add(image);
-    assertEquals(16, image.getOriginLeft());
-    assertEquals(16, image.getOriginTop());
-    assertEquals("clipped", getCurrentImageStateName(image));
-
-    delayTestFinish(5000);
   }
 
   /**
-   * Tests the firing of onload events when 
-   * {@link com.google.gwt.user.client.ui.Image#setUrl(String)}
-   * is called on an unclipped image.
+   * Tests the firing of onload events when
+   * {@link com.google.gwt.user.client.ui.Image#setUrl(String)} is called on an
+   * unclipped image.
+   * 
+   * Disabled because of issue #863
    */
-  /* This test has been commented out because of issue #863
-  public void testSetUrlAndLoadEventsOnUnclippedImage() {
+  public void disabledTestSetUrlAndLoadEventsOnUnclippedImage() {
     final Image image = new Image();
 
     image.addLoadListener(new LoadListener() {
@@ -264,12 +216,122 @@
       }
     });
 
+    delayTestFinish(5000);
     RootPanel.get().add(image);
     image.setUrl("counting-backwards.png");
+  }
+
+  /**
+   * Tests the behavior of
+   * <code>setUrlAndVisibleRect(String, int, int, int, int)</code> method on
+   * an unclipped image, which causes a state transition to the clipped state.
+   * 
+   * Disabled because of issue #863.
+   */
+  public void disabledTestSetUrlAndVisibleRectOnUnclippedImage() {
+    final Image image = new Image("counting-backwards.png");
+
+    image.addLoadListener(new LoadListener() {
+      private int onLoadEventCount = 0;
+
+      public void onError(Widget sender) {
+        fail("The image " + ((Image) sender).getUrl() + " failed to load.");
+      }
+
+      public void onLoad(Widget sender) {
+        if (getCurrentImageStateName(image).equals("unclipped")) {
+          image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16);
+        }
+
+        if (++onLoadEventCount == 2) {
+          assertEquals(0, image.getOriginLeft());
+          assertEquals(16, image.getOriginTop());
+          assertEquals(16, image.getWidth());
+          assertEquals(16, image.getHeight());
+          assertEquals("clipped", getCurrentImageStateName(image));
+          finishTest();
+        }
+      }
+    });
 
     delayTestFinish(5000);
+    RootPanel.get().add(image);
+    assertEquals("unclipped", getCurrentImageStateName(image));
   }
-  */
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTest";
+  }
+
+  /**
+   * Tests the creation of an image in clipped mode.
+   */
+  public void testCreateClippedImage() {
+    final Image image = new Image("counting-forwards.png", 16, 16, 16, 16);
+
+    final TestLoadListener listener = new TestLoadListener(image) {
+      private int onLoadEventCount = 0;
+
+      public void onLoad(Widget sender) {
+        if (++onLoadEventCount == 1) {
+          assertEquals(16, image.getWidth());
+          assertEquals(16, image.getHeight());
+          finish();
+        }
+      }
+    };
+    image.addLoadListener(listener);
+
+    image.addLoadHandler(new LoadHandler() {
+      private int onLoadEventCount = 0;
+
+      public void onLoad(LoadEvent event) {
+        if (++onLoadEventCount == 1) {
+          assertEquals(16, image.getWidth());
+          assertEquals(16, image.getHeight());
+          if (listener.isFinished()) {
+            finishTest();
+          } else {
+            fail("Listener did not fire first");
+          }
+        }
+      }
+    });
+    image.addErrorHandler(new TestErrorHandler(image));
+
+    delayTestFinish(5000);
+    RootPanel.get().add(image);
+    assertEquals(16, image.getOriginLeft());
+    assertEquals(16, image.getOriginTop());
+    assertEquals("clipped", getCurrentImageStateName(image));
+  }
+
+  @SuppressWarnings("deprecation")
+  public void testLoadListenerWiring() {
+    Image im = new Image();
+
+    im.addLoadListener(new LoadListener() {
+
+      public void onError(Widget sender) {
+        ++firedError;
+      }
+
+      public void onLoad(Widget sender) {
+        ++firedLoad;
+      }
+    });
+    im.fireEvent(new LoadEvent() {
+      // Replaced by Joel's event firing when possible.
+    });
+    assertEquals(1, firedLoad);
+    assertEquals(0, firedError);
+    im.fireEvent(new ErrorEvent() {
+      // Replaced by Joel's event firing when possible.
+    });
+    assertEquals(1, firedLoad);
+    assertEquals(1, firedError);
+  }
 
   /**
    * Tests the behavior of
@@ -277,8 +339,7 @@
    * on a clipped image.
    */
   public void testSetUrlAndVisibleRectOnClippedImage() {
-    final Image image = new Image("counting-backwards.png",
-        12, 12, 12, 12);
+    final Image image = new Image("counting-backwards.png", 12, 12, 12, 12);
 
     final TestLoadListener listener = new TestLoadListener(image) {
       private int onLoadEventCount = 0;
@@ -316,46 +377,13 @@
     });
     image.addErrorHandler(new TestErrorHandler(image));
 
+    delayTestFinish(5000);
     RootPanel.get().add(image);
     assertEquals("clipped", getCurrentImageStateName(image));
-    image.setUrlAndVisibleRect("counting-forwards.png",
-        0, 16, 16, 16);
-
-    delayTestFinish(5000);
+    image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16);
   }
 
   /**
-   * Tests the behavior of
-   * <code>setUrlAndVisibleRect(String, int, int, int, int)</code> method on an
-   * unclipped image, which causes a state transition to the clipped state.
-   */
-  /*
-   * This test has been commented out because of issue #863 public void
-   * testSetUrlAndVisibleRectOnUnclippedImage() { final Image image = new
-   * Image("counting-backwards.png");
-   * 
-   * image.addLoadListener(new LoadListener() { private int onLoadEventCount =
-   * 0;
-   * 
-   * public void onError(Widget sender) { fail("The image " + ((Image)
-   * sender).getUrl() + " failed to load."); }
-   * 
-   * public void onLoad(Widget sender) { if
-   * (getCurrentImageStateName(image).equals("unclipped")) {
-   * image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16); }
-   * 
-   * if (++onLoadEventCount == 2) { assertEquals(0, image.getOriginLeft());
-   * assertEquals(16, image.getOriginTop()); assertEquals(16, image.getWidth());
-   * assertEquals(16, image.getHeight()); assertEquals("clipped",
-   * getCurrentImageStateName(image)); finishTest(); } } });
-   * 
-   * RootPanel.get().add(image); assertEquals("unclipped",
-   * getCurrentImageStateName(image));
-   * 
-   * delayTestFinish(5000); }
-   */
-
-  /**
    * Tests the firing of onload events when calling
    * {@link com.google.gwt.user.client.ui.Image#setVisibleRect(int,int,int,int)}
    * on a clipped image.
@@ -389,13 +417,12 @@
     });
     image.addErrorHandler(new TestErrorHandler(image));
 
+    delayTestFinish(5000);
     RootPanel.get().add(image);
     image.setVisibleRect(0, 0, 16, 16);
     image.setVisibleRect(0, 0, 16, 16);
     image.setVisibleRect(16, 0, 16, 16);
     image.setVisibleRect(16, 8, 8, 8);
-
-    delayTestFinish(5000);
   }
 
   /**
@@ -404,8 +431,10 @@
    */
   public void testWrapThenSetUrlAndVisibleRect() {
     String uid = Document.get().createUniqueId();
-    HTML html = new HTML("<img id='" + uid + "' src='counting-backwards.png' width='16' height='16'>");
-    RootPanel.get().add(html);
+    DivElement div = Document.get().createDivElement();
+    div.setInnerHTML("<img id='" + uid
+        + "' src='counting-backwards.png' width='16' height='16'>");
+    Document.get().getBody().appendChild(div);
     final Image image = Image.wrap(Document.get().getElementById(uid));
 
     assertEquals(0, image.getOriginLeft());
@@ -421,34 +450,4 @@
     assertEquals(16, image.getHeight());
     assertEquals("clipped", getCurrentImageStateName(image));
   }
-  
-
-  private int firedLoad;
-  private int firedError;
-  @SuppressWarnings("deprecation")
-  public void testLoadListenerWiring() {
-    Image im = new Image();
-
-    im.addLoadListener(new LoadListener() {
-
-      public void onError(Widget sender) {
-        ++firedError;
-      }
-
-      public void onLoad(Widget sender) {
-        ++firedLoad;
-      }
-    });
-    im.fireEvent(new LoadEvent() {
-      // Replaced by Joel's event firing when possible.
-    });
-    assertEquals(1, firedLoad);
-    assertEquals(0, firedError);
-    im.fireEvent(new ErrorEvent() {
-      // Replaced by Joel's event firing when possible.
-    });
-    assertEquals(1, firedLoad);
-    assertEquals(1, firedError);
-  }
-
 }
diff --git a/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java b/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java
index 566c50e..c223773 100644
--- a/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java
+++ b/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java
@@ -15,15 +15,29 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.dom.client.LabelElement;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
 
 /**
  * Tests the RadioButton class.
  */
 public class RadioButtonTest extends GWTTestCase {
 
+  private static class Changeable implements ValueChangeHandler<Boolean> {
+    Boolean received;
+
+    public void onValueChange(ValueChangeEvent<Boolean> event) {
+      received = event.getValue();
+    }
+  }
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.user.DebugTest";
@@ -33,7 +47,7 @@
     RadioButton radio = new RadioButton("myName", "myLabel");
 
     // We need to replace the input element so we can keep a handle to it
-    Element newInput = DOM.createInputRadio("MyName");
+    com.google.gwt.user.client.Element newInput = DOM.createInputRadio("MyName");
     radio.replaceInputElement(newInput);
 
     radio.ensureDebugId("myRadio");
@@ -45,6 +59,39 @@
   }
 
   /**
+   * Test the name and grouping methods.
+   */
+  public void testGrouping() {
+    // Create some radio buttons
+    RadioButton r1 = new RadioButton("group1", "Radio 1");
+    RadioButton r2 = new RadioButton("group1", "Radio 2");
+    RadioButton r3 = new RadioButton("group2", "Radio 3");
+    RootPanel.get().add(r1);
+    RootPanel.get().add(r2);
+    RootPanel.get().add(r3);
+
+    // Check one button in each group
+    r2.setValue(true);
+    r3.setValue(true);
+
+    // Move a button over
+    r2.setName("group2");
+
+    // Check that the correct buttons are checked
+    assertTrue(r2.getValue());
+    assertFalse(r3.getValue());
+
+    r1.setValue(true);
+    assertTrue(r1.getValue());
+    assertTrue(r2.getValue());
+
+    r3.setValue(true);
+    assertTrue(r1.getValue());
+    assertFalse(r2.getValue());
+    assertTrue(r3.getValue());
+  }
+
+  /**
    * Test the name and grouping methods via deprecated calls.
    */
   @SuppressWarnings("deprecation")
@@ -77,39 +124,6 @@
     assertFalse(r2.isChecked());
     assertTrue(r3.isChecked());
   }
-  
-  /**
-   * Test the name and grouping methods.
-   */
-  public void testGrouping() {
-    // Create some radio buttons
-    RadioButton r1 = new RadioButton("group1", "Radio 1");
-    RadioButton r2 = new RadioButton("group1", "Radio 2");
-    RadioButton r3 = new RadioButton("group2", "Radio 3");
-    RootPanel.get().add(r1);
-    RootPanel.get().add(r2);
-    RootPanel.get().add(r3);
-
-    // Check one button in each group
-    r2.setValue(true);
-    r3.setValue(true);
-
-    // Move a button over
-    r2.setName("group2");
-
-    // Check that the correct buttons are checked
-    assertTrue(r2.getValue());
-    assertFalse(r3.getValue());
-
-    r1.setValue(true);
-    assertTrue(r1.getValue());
-    assertTrue(r2.getValue());
-
-    r3.setValue(true);
-    assertTrue(r1.getValue());
-    assertFalse(r2.getValue());
-    assertTrue(r3.getValue());
-  }
 
   /**
    * Ensures that the element order doesn't get reversed when the radio's
@@ -128,4 +142,95 @@
     assertEquals("input", firstChild.getTagName().toLowerCase());
     assertEquals("label", secondChild.getTagName().toLowerCase());
   }
+
+// TODO: Re-enable these tests when we figure out how to make them work
+// properly on IE (which has the unfortunate property of not passing
+// synthesized events on to native controls, keeping the clicks created by
+// these tests from actually affecting the radio buttons' states).
+//
+//  public void testValueChangeViaClick() {
+//    RadioButton r1 = new RadioButton("group1", "Radio 1");
+//    RadioButton r2 = new RadioButton("group1", "Radio 2");
+//    RootPanel.get().add(r1);
+//    RootPanel.get().add(r2);
+//    r1.setValue(true);
+//
+//    Changeable c1 = new Changeable();
+//    r1.addValueChangeHandler(c1);
+//
+//    Changeable c2 = new Changeable();
+//    r2.addValueChangeHandler(c2);
+//
+//    // Brittle, but there's no public access
+//    InputElement r1Radio = getRadioElement(r1);
+//    InputElement r2Radio = getRadioElement(r2);
+//
+//    doClick(r1Radio);
+//    assertEquals(null, c1.received);
+//    assertEquals(null, c2.received);
+//
+//    doClick(r2Radio);
+//    assertEquals(null, c1.received);
+//    assertEquals(Boolean.TRUE, c2.received);
+//    c2.received = null;
+//
+//    doClick(r1Radio);
+//    assertEquals(Boolean.TRUE, c1.received);
+//    assertEquals(null, c2.received);
+//  }
+//
+//  public void testValueChangeViaLabelClick() {
+//    RadioButton r1 = new RadioButton("group1", "Radio 1");
+//    RadioButton r2 = new RadioButton("group1", "Radio 2");
+//    RootPanel.get().add(r1);
+//    RootPanel.get().add(r2);
+//    r1.setValue(true);
+//
+//    Changeable c1 = new Changeable();
+//    r1.addValueChangeHandler(c1);
+//
+//    Changeable c2 = new Changeable();
+//    r2.addValueChangeHandler(c2);
+//
+//    LabelElement r1Label = getLabelElement(r1);
+//    LabelElement r2Label = getLabelElement(r2);
+//
+//    doClick(r1Label);
+//    assertEquals(null, c1.received);
+//    assertEquals(null, c2.received);
+//
+//    doClick(r2Label);
+//    assertEquals(null, c1.received);
+//    assertEquals(Boolean.TRUE, c2.received);
+//    c2.received = null;
+//
+//    doClick(r1Label);
+//    assertEquals(Boolean.TRUE, c1.received);
+//    assertEquals(null, c2.received);
+//  }
+//
+//  private void doClick(Element elm) {
+//    NativeEvent e = Document.get().createMouseDownEvent(0, 25, 25, 25, 25,
+//        false, false, false, false, NativeEvent.BUTTON_LEFT);
+//    elm.dispatchEvent(e);
+//
+//    e = Document.get().createMouseUpEvent(0, 25, 25, 25, 25, false, false,
+//        false, false, NativeEvent.BUTTON_LEFT);
+//    elm.dispatchEvent(e);
+//
+//    e = Document.get().createClickEvent(0, 25, 25, 25, 25, false, false, false,
+//        false);
+//    elm.dispatchEvent(e);
+//  }
+//
+//  private LabelElement getLabelElement(RadioButton radioButton) {
+//    LabelElement r1Label = LabelElement.as(Element.as(getRadioElement(
+//        radioButton).getNextSiblingElement()));
+//    return r1Label;
+//  }
+//
+//  private InputElement getRadioElement(RadioButton radioButton) {
+//    InputElement r1Radio = InputElement.as(Element.as(radioButton.getElement().getFirstChild()));
+//    return r1Radio;
+//  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/RootPanelTest.java b/user/test/com/google/gwt/user/client/ui/RootPanelTest.java
new file mode 100644
index 0000000..8d985b4
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/RootPanelTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 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.ui;
+
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests {@link RootPanel}.
+ */
+public class RootPanelTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  /**
+   * Ensures that {@link RootPanel#get(String)} behaves properly.
+   */
+  public void testGetById() {
+    Document doc = Document.get();
+    DivElement div = doc.createDivElement();
+    doc.getBody().appendChild(div);
+    div.setInnerHTML("<div id='a'></div><div id='b'></div>");
+
+    // You should get the same RootPanel for subsequent calls to get() with the
+    // same id. But you should get *different* RootPanels for calls with
+    // different ids.
+    RootPanel aRoot = RootPanel.get("a");
+    RootPanel bRoot = RootPanel.get("b");
+
+    assertSame(
+        "RootPanel.get() should return the same instancefor the same id",
+        aRoot, RootPanel.get("a"));
+    assertSame(
+        "RootPanel.get() should return the same instancefor the same id",
+        bRoot, RootPanel.get("b"));
+    assertNotSame("RootPanels a and b should be different", aRoot, bRoot);
+
+    // If a RootPanel's element is replaced in the DOM, you should get a
+    // new RootPanel instance if you ask for it again (see issue 1937).
+    Element aElem = doc.getElementById("a");
+    Element newAElem = doc.createDivElement();
+    newAElem.setId("a");
+    aElem.getParentElement().replaceChild(newAElem, aElem);
+
+    RootPanel newARoot = RootPanel.get("a");
+    assertNotSame("New RootPanel should not be same as old", newARoot, aRoot);
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java b/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
index 96e2f3c..feb84c1 100644
--- a/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.util.Arrays;
@@ -110,7 +112,31 @@
     RootPanel.get().clear();
   }
 
+  public void testWrapUsingStaticWrapMethod() {
+    Element wrapper = Document.get().createTextInputElement();
+    RootPanel.get().getElement().appendChild(wrapper);
+
+    // Use direct wrap method from suggest box.
+    SuggestBox box = SuggestBox.wrap(createOracle(), wrapper);
+    assertTrue(box.isAttached());
+    assertTrue(box.getWidget().getParent() == box);
+  }
+
+  public void testWrapUsingComposite() {
+    // Ensure we can use this with normal composites
+    Element wrapper = Document.get().createTextInputElement();
+    RootPanel.get().getElement().appendChild(wrapper);
+    TextBox b = TextBox.wrap(wrapper);
+    SuggestBox box = new SuggestBox(createOracle(), b);
+    assertTrue(b.getParent() == box);
+  }
+
   protected SuggestBox createSuggestBox() {
+    MultiWordSuggestOracle oracle = createOracle();
+    return new SuggestBox(oracle);
+  }
+
+  private MultiWordSuggestOracle createOracle() {
     MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
     oracle.add("test");
     oracle.add("test1");
@@ -118,6 +144,6 @@
     oracle.add("test3");
     oracle.add("test4");
     oracle.add("john");
-    return new SuggestBox(oracle);
+    return oracle;
   }
 }
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java b/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
index 73bd3fe..5aa9aa9 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
@@ -53,7 +53,6 @@
     Object params2[] = new Object[] {"ab\"cde\"fg", 1234};
     RPCRequest request2 = new RPCRequest(method, params2, null, 0);
     String strRequest2 = request2.toString();
-    System.out.println(strRequest2);
     assertEquals("com.google.gwt.user.server.rpc.RPCRequestTest$"
         + "MockRequestImplementation.doSomething(\"ab\\\"cde\\\"fg\", 1234)",
         strRequest2);