Merging releases/1.5@r3078:r3125 into trunk.

svn merge -r3078:3125 https://google-web-toolkit.googlecode.com/svn/releases/1.5 .


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3126 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeTemplate.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeTemplate.js
index 8f2c3b9..e60fd20 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeTemplate.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeTemplate.js
@@ -20,7 +20,6 @@
   // Cache symbols locally for good obfuscation
   var $wnd = window
   ,$doc = document
-  ,external = $wnd.external
   ,$stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null
 
   // These variables gate calling gwtOnLoad; all must be true to start
@@ -71,7 +70,7 @@
 
   function isHostedMode() {
     try {
-      return (external && external.gwtOnLoad &&
+      return ($wnd.external && $wnd.external.gwtOnLoad &&
           ($wnd.location.search.indexOf('gwt.hybrid') == -1));
     } catch (e) {
       // Defensive: some versions of IE7 reportedly can throw an exception
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
index ecbfd30..6b7d755 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -332,6 +332,11 @@
       if (isParameterized != null && isParameterized.getBaseType() == type) {
         return isParameterized;
       }
+      
+      JRawType isRaw = supertype.isRawType();
+      if (isRaw != null && isRaw.getBaseType() == type) {
+        return isRaw.asParameterizedByWildcards();
+      }
     }
 
     return null;
@@ -355,6 +360,7 @@
 
   public abstract JClassType getEnclosingType();
 
+  @Override
   public abstract JClassType getErasedType();
 
   public abstract JField getField(String name);
@@ -471,6 +477,7 @@
 
   public abstract boolean isFinal();
 
+  @Override
   public abstract JGenericType isGenericType();
 
   @Override
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
index 68d3f1f..b914c76 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -40,6 +42,15 @@
     }
   }
 
+  public JParameterizedType asParameterizedByWildcards() {
+    JClassType[] typeArgs = new JClassType[typeParams.size()];
+    for (int i = 0; i < typeArgs.length; ++i) {
+      typeArgs[i] = getOracle().getWildcardType(BoundType.EXTENDS,
+          typeParams.get(i).getFirstBound());
+    }
+    return getOracle().getParameterizedType(this, typeArgs);
+  }
+
   @Override
   public JClassType getErasedType() {
     return getRawType();
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
index b35075d..9b6ea87 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
@@ -37,6 +37,10 @@
     members = new DelegateMembers(this, getBaseType(), ERASURE_SUBSTITUTION);
   }
 
+  public JParameterizedType asParameterizedByWildcards() {
+    return getBaseType().asParameterizedByWildcards();
+  }
+
   @Override
   public JConstructor findConstructor(JType[] paramTypes) {
     return members.findConstructor(paramTypes);
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
index bb5a33e..7681004 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
@@ -39,9 +39,9 @@
     UNBOUND
   }
 
+  private final BoundType boundType;
   private JClassType[] lazyLowerBounds;
   private JClassType[] lazyUpperBounds;
-  private final BoundType boundType;
 
   public JWildcardType(BoundType boundType, JClassType typeBound) {
     this.boundType = boundType;
@@ -58,15 +58,13 @@
     return getBaseType().findMethod(name, paramTypes);
   }
 
+  public BoundType getBoundType() {
+    return boundType;
+  }
+
   @Override
   public JClassType getErasedType() {
-    if (isUpperBound()) {
-      // ? extends T erases to T
-      return getFirstBound().getErasedType();
-    }
-
-    // ? super T erases to Object
-    return getOracle().getJavaLangObject();
+    return getUpperBound().getErasedType();
   }
 
   @Override
@@ -145,6 +143,14 @@
     return getOracle().getJavaLangObject();
   }
 
+  public JClassType getUpperBound() {
+    if (isUpperBound()) {
+      return getFirstBound();
+    }
+
+    return getOracle().getJavaLangObject();
+  }
+
   /**
    * Returns the upper bounds of this wildcard type. If no upper bounds were
    * declared, an array containing {@link Object} is returned.
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index d3dcd7a..e217ce2 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -167,6 +167,8 @@
     }
   }
 
+  private Set<JClassType> allTypes = null;
+
   private final Map<JType, JArrayType> arrayTypes = new IdentityHashMap<JType, JArrayType>();
 
   private final Set<JRealClassType> invalidatedTypes = new HashSet<JRealClassType>();
@@ -455,14 +457,16 @@
    * @return an array of types, possibly of zero length
    */
   public JClassType[] getTypes() {
-    Set<JClassType> allTypes = new HashSet<JClassType>();
-    JPackage[] pkgs = getPackages();
-    for (int i = 0; i < pkgs.length; i++) {
-      JPackage pkg = pkgs[i];
-      JClassType[] types = pkg.getTypes();
-      for (int j = 0; j < types.length; j++) {
-        JRealClassType type = (JRealClassType) types[j];
-        buildAllTypesImpl(allTypes, type);
+    if (allTypes == null) {
+      allTypes = new HashSet<JClassType>();
+      JPackage[] pkgs = getPackages();
+      for (int i = 0; i < pkgs.length; i++) {
+        JPackage pkg = pkgs[i];
+        JClassType[] types = pkg.getTypes();
+        for (int j = 0; j < types.length; j++) {
+          JRealClassType type = (JRealClassType) types[j];
+          buildAllTypesImpl(allTypes, type);
+        }
       }
     }
     return allTypes.toArray(NO_JCLASSES);
@@ -533,6 +537,7 @@
    * TODO: make this not public.
    */
   public void refresh(TreeLogger logger) throws NotFoundException {
+    allTypes = null;
     if (javaLangObject == null) {
       javaLangObject = findType("java.lang.Object");
       if (javaLangObject == null) {
@@ -633,9 +638,9 @@
     // For each type, walk up its hierarchy chain and tell each supertype
     // about its subtype.
     //
-    JClassType[] allTypes = getTypes();
-    for (int i = 0; i < allTypes.length; i++) {
-      JClassType type = allTypes[i];
+    JClassType[] types = getTypes();
+    for (int i = 0; i < types.length; i++) {
+      JClassType type = types[i];
       type.notifySuperTypes();
     }
   }
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
index 29d93f1..177eaa9 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
@@ -20,7 +20,6 @@
   // Cache symbols locally for good obfuscation
   var $wnd = window
   ,$doc = document
-  ,external = $wnd.external
   ,$stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null
 
   // These variables gate calling gwtOnLoad; all must be true to start
@@ -67,7 +66,7 @@
 
   function isHostedMode() {
     try {
-      return (external && external.gwtOnLoad &&
+      return ($wnd.external && $wnd.external.gwtOnLoad &&
           ($wnd.location.search.indexOf('gwt.hybrid') == -1));
     } catch (e) {
       // Defensive: some versions of IE7 reportedly can throw an exception
diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js b/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js
index d4b02f5..b2aed9b 100644
--- a/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js
@@ -20,7 +20,6 @@
   // Cache symbols locally for good obfuscation
   var $wnd = window
   ,$doc = document
-  ,external = $wnd.external
   
   // These variables gate calling gwtOnLoad; all must be true to start
   ,gwtOnLoad, bodyDone
@@ -58,7 +57,7 @@
 
   function isHostedMode() {
     try {
-      return (external && external.gwtOnLoad &&
+      return ($wnd.external && $wnd.external.gwtOnLoad &&
           ($wnd.location.search.indexOf('gwt.hybrid') == -1));
     } catch (e) {
       // Defensive: some versions of IE7 reportedly can throw an exception
diff --git a/dev/core/src/com/google/gwt/core/linker/XSTemplate.js b/dev/core/src/com/google/gwt/core/linker/XSTemplate.js
index 0e4e4cc..826d553 100644
--- a/dev/core/src/com/google/gwt/core/linker/XSTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/XSTemplate.js
@@ -20,7 +20,6 @@
   // Cache symbols locally for good obfuscation
   var $wnd = window
   ,$doc = document
-  ,external = $wnd.external
   ,$stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null
 
   // These variables gate calling gwtOnLoad; all must be true to start
@@ -67,7 +66,7 @@
 
   function isHostedMode() {
     try {
-      return (external && external.gwtOnLoad &&
+      return ($wnd.external && $wnd.external.gwtOnLoad &&
           ($wnd.location.search.indexOf('gwt.hybrid') == -1));
     } catch (e) {
       // Defensive: some versions of IE7 reportedly can throw an exception
diff --git a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
index a221bcb..4462ec7 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
@@ -20,6 +20,7 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.shell.StandardGeneratorContext;
+import com.google.gwt.dev.util.PerfLogger;
 
 /**
  * A rule to replace the type being rebound with a class whose name is
@@ -45,7 +46,10 @@
     }
 
     long before = System.currentTimeMillis();
+    PerfLogger.start("Generator " + generator.getClass().getCanonicalName()
+        + " for " + typeName);
     String className = generator.generate(logger, context, typeName);
+    PerfLogger.end();
     long after = System.currentTimeMillis();
     if (className == null) {
       msg = "Generator returned null, so the requested type will be used as is";
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
index e374a06..3b69128 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
@@ -21,6 +21,7 @@
 import com.google.gwt.dev.javac.CompilationUnit.State;
 import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.util.PerfLogger;
 
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.ClassFile;
@@ -95,31 +96,28 @@
    * compile errors.
    */
   public void compile(TreeLogger logger) throws UnableToCompleteException {
+    PerfLogger.start("CompilationState.compile");
     Set<CompilationUnit> units = getCompilationUnits();
-    JdtCompiler.compile(units);
-    Set<String> validBinaryTypeNames = getValidBinaryTypeNames(units);
+    if (JdtCompiler.compile(units)) {
+      Set<String> validBinaryTypeNames = getValidBinaryTypeNames(units);
 
-    // Dump all units with direct errors; we cannot safely check them.
-    boolean anyErrors = CompilationUnitInvalidator.invalidateUnitsWithErrors(
-        logger, units);
+      // Dump all units with direct errors; we cannot safely check them.
+      boolean anyErrors = CompilationUnitInvalidator.invalidateUnitsWithErrors(
+          logger, units);
 
-    // Check all units using our custom checks.
-    CompilationUnitInvalidator.validateCompilationUnits(units,
-        validBinaryTypeNames);
+      // Check all units using our custom checks.
+      CompilationUnitInvalidator.validateCompilationUnits(units,
+          validBinaryTypeNames);
 
-    // More units may have errors now.
-    anyErrors |= CompilationUnitInvalidator.invalidateUnitsWithErrors(logger,
-        units);
+      // More units may have errors now.
+      anyErrors |= CompilationUnitInvalidator.invalidateUnitsWithErrors(logger,
+          units);
 
-    if (anyErrors) {
-      CompilationUnitInvalidator.invalidateUnitsWithInvalidRefs(logger, units);
-    }
+      if (anyErrors) {
+        CompilationUnitInvalidator.invalidateUnitsWithInvalidRefs(logger, units);
+      }
 
-    JsniCollector.collectJsniMethods(logger, units, new JsProgram());
-
-    // JSNI collection can generate additional errors.
-    if (CompilationUnitInvalidator.invalidateUnitsWithErrors(logger, units)) {
-      CompilationUnitInvalidator.invalidateUnitsWithInvalidRefs(logger, units);
+      JsniCollector.collectJsniMethods(logger, units, new JsProgram());
     }
 
     mediator.refresh(logger, units);
@@ -132,6 +130,7 @@
     }
 
     updateExposedUnits();
+    PerfLogger.end();
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index 5a6748a..3d303f7 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -39,8 +39,37 @@
  */
 public abstract class CompilationUnit {
 
+  /**
+   * Tracks the state of a compilation unit through the compile and recompile
+   * process.
+   */
   enum State {
-    COMPILED, CHECKED, ERROR, FRESH
+    /**
+     * All internal state is cleared; the unit's source has not yet been
+     * compiled by JDT.
+     */
+    FRESH,
+    /**
+     * In this intermediate state, the unit's source has been compiled by JDT.
+     * The unit will contain a set of CompiledClasses.
+     */
+    COMPILED,
+    /**
+     * In this final state, the unit was compiled, but contained one or more
+     * errors. Those errors are cached inside the unit, but all other internal
+     * state is cleared.
+     */
+    ERROR,
+    /**
+     * In this final state, the unit has been compiled and is error free.
+     * Additionally, all other units this unit depends on (transitively) are
+     * also error free. The unit contains a set of checked CompiledClasses. The
+     * unit and each contained CompiledClass releases all references to the JDT
+     * AST. Each class contains a reference to a valid JRealClassType, which has
+     * been added to the module's TypeOracle, as well as byte code, JSNI
+     * methods, and all other final state.
+     */
+    CHECKED
   }
 
   private class FindTypesInCud extends ASTVisitor {
@@ -209,6 +238,7 @@
    * Sets the compiled JDT AST for this unit.
    */
   void setJdtCud(CompilationUnitDeclaration cud) {
+    assert (state == State.FRESH || state == State.ERROR);
     this.cud = cud;
     state = State.COMPILED;
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index 831b46a..79bd184 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -189,10 +189,8 @@
    * Compiles the given set of units. The units will be internally modified to
    * reflect the results of compilation.
    */
-  public static void compile(Collection<CompilationUnit> units) {
-    PerfLogger.start("JdtCompiler.compile");
-    new JdtCompiler().doCompile(units);
-    PerfLogger.end();
+  public static boolean compile(Collection<CompilationUnit> units) {
+    return new JdtCompiler().doCompile(units);
   }
 
   private static CompilerOptions getCompilerOptions() {
@@ -255,7 +253,7 @@
     }
   }
 
-  private void doCompile(Collection<CompilationUnit> units) {
+  private boolean doCompile(Collection<CompilationUnit> units) {
     List<ICompilationUnit> icus = new ArrayList<ICompilationUnit>();
     for (CompilationUnit unit : units) {
       String packageName = Shared.getPackageName(unit.getTypeName());
@@ -271,9 +269,14 @@
         }
       }
     }
-    if (!icus.isEmpty()) {
-      compiler.compile(icus.toArray(new ICompilationUnit[icus.size()]));
+    if (icus.isEmpty()) {
+      return false;
     }
+
+    PerfLogger.start("JdtCompiler.compile");
+    compiler.compile(icus.toArray(new ICompilationUnit[icus.size()]));
+    PerfLogger.end();
+    return true;
   }
 
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
index 4987620..fc3bc02 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
@@ -70,9 +70,9 @@
         case SHL:
         case SHR:
         case SHRU:
-          if (rhsType != program.getTypePrimitiveInt()) {
+          if (rhsType == longType) {
             throw new InternalCompilerException(
-                "Expected right operand to be of type int");
+                "Expected right operand not to be of type long");
           }
           break;
         default:
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
index d2d4d36..2f1a8e0 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
@@ -42,17 +42,54 @@
   }
 
   private void ctorAddKnownGlobalSymbols() {
+    // Section references are from Ecma-262
+    // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)
     String[] commonBuiltins = new String[] {
-        "ActiveXObject", "Array", "Boolean", "Date", "Debug", "Enumerator",
-        "Error", "Function", "Global", "Image", "Math", "Number", "Object",
-        "RegExp", "String", "VBArray", "window", "document", "event",
-        "arguments", "call", "toString", "$wnd", "$doc", "$moduleName",
-        "$moduleBase", "undefined", "getClass", "$gwt_version"};
+        // 15.1.1 Value Properties of the Global Object
+        "NaN",
+        "Infinity",
+        "undefined",
+
+        // 15.1.2 Function Properties of the Global Object
+        "eval", "parseInt", "parseFloat",
+        "isNan",
+        "isFinite",
+
+        // 15.1.3 URI Handling Function Properties
+        "decodeURI", "decodeURIComponent",
+        "encodeURI",
+        "encodeURIComponent",
+
+        // 15.1.4 Constructor Properties of the Global Object
+        "Object", "Function", "Array", "String", "Boolean", "Number", "Date",
+        "RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
+        "SyntaxError", "TypeError", "URIError",
+
+        // 15.1.5 Other Properties of the Global Object
+        "Math",
+
+        // 10.1.6 Activation Object
+        "arguments",
+
+        // B.2 Additional Properties (non-normative)
+        "escape", "unescape",
+
+        // Common browser-defined identifiers not defined in ECMAScript
+        "window", "document", "event", "location", "history", "external",
+        "Debug", "Enumerator", "Global", "Image", "ActiveXObject", "VBArray",
+
+        // Functions commonly defined on Object
+        "toString", "getClass",
+
+        // GWT-defined identifiers
+        "$wnd", "$doc", "$moduleName", "$moduleBase", "$gwt_version",
+
+        // TODO: prove why this is necessary or remove it
+        "call",};
 
     for (int i = 0; i < commonBuiltins.length; i++) {
       String ident = commonBuiltins[i];
       this.doCreateName(ident, ident);
     }
   }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/util/PerfLogger.java b/dev/core/src/com/google/gwt/dev/util/PerfLogger.java
index 31f5965..5404539 100644
--- a/dev/core/src/com/google/gwt/dev/util/PerfLogger.java
+++ b/dev/core/src/com/google/gwt/dev/util/PerfLogger.java
@@ -142,7 +142,7 @@
       msg.append(t.message);
       if (!t.messageOnly) {
         msg.append(" ");
-        msg.append(t.totalTimeNanos / 1000000.0);
+        msg.append(t.totalTimeNanos / 1000000);
         msg.append("ms");
       }
       System.out.println(msg);
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
index 4d86ba6..db43c0f 100644
--- a/dev/core/super/com/google/gwt/lang/LongLib.java
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -45,6 +45,10 @@
    * implementation of the long type for web mode, any place it uses a long is
    * not usable in web mode. There are currently two such methods:
    * {@link #toLong(double[])} and {@link #make(long)}.
+   * 
+   * The GWT RPC serialization code is dependent on the internal format of the
+   * long type; any changes made to this class should be reflected in the
+   * implementations of SerializationStreamReader and Writer.
    */
 
   /**
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java
index 46a22be..e950b20 100644
--- a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/DefaultMuseum.java
@@ -28,6 +28,7 @@
     addIssue(new Issue1245());

     addIssue(new Issue1772());

     addIssue(new Issue1897());

+    addIssue(new Issue1932());

     addIssue(new Issue2261());

     addIssue(new Issue2290());

     addIssue(new Issue2307());

diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue1932.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue1932.java
new file mode 100644
index 0000000..7233d75
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue1932.java
@@ -0,0 +1,160 @@
+/*
+ * 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.museum.client.defaultmuseum;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.EventPreview;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * DOM.eventGetClientX/Y incorrect with HTML margin/borders Firefox 2/Safari 3.
+ */
+public class Issue1932 extends AbstractIssue {
+  /**
+   * A set of options used to set the page margins and borders
+   */
+  private class ControlPanel extends Composite {
+    private final Grid grid = new Grid(2, 3);
+
+    private final TextBox borderBox = new TextBox();
+
+    private final TextBox marginBox = new TextBox();
+
+    public ControlPanel() {
+      initWidget(grid);
+
+      // Add an option to set the margin
+      marginBox.setText("10px");
+      grid.setHTML(0, 0, "<b>Margin:</b>");
+      grid.setWidget(0, 1, marginBox);
+      grid.setWidget(0, 2, new Button("Set", new ClickListener() {
+        public void onClick(Widget sender) {
+          updateMargin();
+        }
+      }));
+
+      // Add an option to set the border
+      borderBox.setText("5px solid #DDDDDD");
+      grid.setHTML(1, 0, "<b>Border:</b>");
+      grid.setWidget(1, 1, borderBox);
+      grid.setWidget(1, 2, new Button("Set", new ClickListener() {
+        public void onClick(Widget sender) {
+          updateBorder();
+        }
+      }));
+    }
+
+    /**
+     * Update the border on the HTML element.
+     */
+    public void updateBorder() {
+      htmlElement.getStyle().setProperty("border", borderBox.getText());
+    }
+
+    /**
+     * Update the margin on the HTML element.
+     */
+    public void updateMargin() {
+      htmlElement.getStyle().setProperty("margin", marginBox.getText());
+    }
+  }
+
+  /**
+   * The HTML element of the page.
+   */
+  private Element htmlElement = null;
+
+  @Override
+  public Widget createIssue() {
+    // Setup the page size and cursor
+    Element bodyElement = RootPanel.getBodyElement();
+    htmlElement = DOM.getParent(RootPanel.getBodyElement());
+
+    // Create a crosshair to show the current position
+    final SimplePanel positioner = new SimplePanel();
+    positioner.setPixelSize(30, 30);
+    positioner.getElement().getStyle().setProperty("borderLeft",
+        "1px solid red");
+    positioner.getElement().getStyle().setProperty("borderTop", "1px solid red");
+    positioner.getElement().getStyle().setProperty("cursor", "crosshair");
+
+    // Create a target box to test inside
+    final Label sandbox = new Label();
+    sandbox.sinkEvents(Event.ONMOUSEMOVE);
+    sandbox.setPixelSize(300, 300);
+    sandbox.getElement().getStyle().setProperty("border", "3px solid blue");
+    sandbox.getElement().getStyle().setProperty("cursor", "crosshair");
+
+    // Keep the crosshair under the cursor
+    DOM.addEventPreview(new EventPreview() {
+      public boolean onEventPreview(Event event) {
+        // Ignore events outside of the sandbox
+        Element target = DOM.eventGetTarget(event);
+        if (!sandbox.getElement().isOrHasChild(target)
+            && !positioner.getElement().isOrHasChild(target)) {
+          positioner.removeFromParent();
+          return true;
+        }
+
+        switch (DOM.eventGetType(event)) {
+          case Event.ONMOUSEMOVE:
+            RootPanel.get().add(positioner, event.getClientX(),
+                event.getClientY());
+            break;
+        }
+        return true;
+      }
+    });
+
+    // Combine the control panel and return
+    VerticalPanel vPanel = new VerticalPanel();
+    vPanel.add(new ControlPanel());
+    vPanel.add(sandbox);
+    return vPanel;
+  }
+
+  @Override
+  public String getInstructions() {
+    return "Move the cursor inside the blue box below and verify that the "
+        + "point of the red positioner lines up directly beneath the center of "
+        + "the cursor (crosshair).  The buttons may not work on Safari 2 "
+        + "because Safari 2 has issues when you attempt to modify the HTML "
+        + "element programatically.";
+  }
+
+  @Override
+  public String getSummary() {
+    return "DOM.eventGetClientX/Y incorrect with HTML margin/border in Firefox "
+        + "2 and Safari 2";
+  }
+
+  @Override
+  public boolean hasCSS() {
+    return true;
+  }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/TestFireEvents.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/TestFireEvents.java
index 9f5bdca..f67db1c 100644
--- a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/TestFireEvents.java
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/TestFireEvents.java
@@ -58,6 +58,30 @@
     Button button = new Button("Double-click me") {
       @Override
       public void onBrowserEvent(Event event) {
+        // Verify that values associated with events are defined. For some
+        // values, we just want to make sure we can get them without any
+        // errrors.
+        assert event.getClientX() > 0;
+        assert event.getClientY() > 0;
+        assert event.getScreenX() > 0;
+        assert event.getScreenY() > 0;
+        event.getAltKey();
+        event.getCtrlKey();
+        event.getShiftKey();
+        event.getMetaKey();
+        int eventType = event.getTypeInt();
+        switch (eventType) {
+          case Event.ONMOUSEDOWN:
+          case Event.ONMOUSEUP:
+            event.getButton();
+            break;
+          case Event.ONMOUSEOVER:
+          case Event.ONMOUSEOUT:
+            assert event.getFromElement() != null;
+            assert event.getToElement() != null;
+            break;
+        }
+
         passTest(event);
       }
     };
@@ -72,8 +96,30 @@
 
     // Keyboard events
     TextBox textBox = new TextBox() {
+      @SuppressWarnings("fallthrough")
       @Override
       public void onBrowserEvent(Event event) {
+        // Verify that values associated with events are defined
+        int eventType = event.getTypeInt();
+        switch (eventType) {
+          case Event.ONFOCUS:
+          case Event.ONBLUR:
+            break;
+          case Event.ONKEYDOWN:
+            event.getRepeat();
+            // Intentional fall through
+          case Event.ONKEYUP:
+          case Event.ONKEYPRESS:
+            event.getAltKey();
+            event.getCtrlKey();
+            event.getShiftKey();
+            event.getMetaKey();
+            assert event.getKeyCode() > 0;
+            break;
+          case Event.ONCHANGE:
+            break;
+        }
+
         passTest(event);
       }
     };
@@ -96,6 +142,20 @@
     ScrollPanel scrollable = new ScrollPanel(scrollableContents) {
       @Override
       public void onBrowserEvent(Event event) {
+        // Verify that values associated with events are defined
+        int eventType = event.getTypeInt();
+        switch (eventType) {
+          case Event.ONMOUSEWHEEL:
+            event.getClientX();
+            event.getClientY();
+            event.getScreenX();
+            event.getScreenY();
+            event.getMouseWheelVelocityY();
+            break;
+          case Event.ONSCROLL:
+            break;
+        }
+
         passTest(event);
       }
     };
@@ -110,13 +170,29 @@
       @Override
       public void onBrowserEvent(Event event) {
         passTest(event);
-        if (DOM.eventGetType(event) == Event.ONERROR) {
-          setUrl("issues/images/gwtLogo.png");
+
+        int eventType = event.getTypeInt();
+        switch (eventType) {
+          case Event.ONERROR:
+            setUrl("issues/images/gwtLogo.png");
+            break;
+          case Event.ONCONTEXTMENU:
+            assert event.getClientX() > 0;
+            assert event.getClientY() > 0;
+            assert event.getScreenX() > 0;
+            assert event.getScreenY() > 0;
+            event.getAltKey();
+            event.getShiftKey();
+            event.getCtrlKey();
+            event.getMetaKey();
+            break;
         }
       }
     };
+    loadable.sinkEvents(Event.ONCONTEXTMENU);
     addTest(Event.ONERROR, "error", loadable);
     addDependentTest(Event.ONLOAD, "load");
+    addDependentTest(Event.ONCONTEXTMENU, "contextMenu");
     loadable.setUrl("imageDoesNotExist.abc");
 
     // The following are not testable or not supported in all browsers
diff --git a/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue1932.css b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue1932.css
new file mode 100644
index 0000000..129ffe9
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue1932.css
@@ -0,0 +1,6 @@
+@import url("MasterIssue.css");

+
+HTML {
+  margin: 10px;
+  border: 5px solid #DDDDDD;
+}

diff --git a/user/src/com/google/gwt/benchmarks/BenchmarkShell.java b/user/src/com/google/gwt/benchmarks/BenchmarkShell.java
index d556134..c916dc6 100644
--- a/user/src/com/google/gwt/benchmarks/BenchmarkShell.java
+++ b/user/src/com/google/gwt/benchmarks/BenchmarkShell.java
@@ -45,7 +45,9 @@
     }
 
     public void processResult(TestCase testCase, JUnitResult result) {
-      report.addBenchmarkResults(testCase, (BenchmarkResults) result);
+      if (result instanceof BenchmarkResults) {
+        report.addBenchmarkResults(testCase, (BenchmarkResults) result);
+      }
     }
   }
 
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 5e8d86d..4811501 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -92,8 +92,7 @@
           "ERROR: GWT.create() is only usable in client code!  It cannot be called, "
               + "for example, from server code.  If you are running a unit test, "
               + "check that your test case extends GWTTestCase and that GWT.create() "
-              + "is not called from within an initializer, constructor, or "
-              + "setUp()/tearDown().");
+              + "is not called from within an initializer or constructor.");
     } else {
       return sGWTBridge.<T> create(classLiteral);
     }
diff --git a/user/src/com/google/gwt/core/client/JsArray.java b/user/src/com/google/gwt/core/client/JsArray.java
new file mode 100644
index 0000000..4008079
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/JsArray.java
@@ -0,0 +1,74 @@
+/*
+ * 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.core.client;
+
+/**
+ * A simple wrapper around a homogeneous native array of
+ * {@link JavaScriptObject} values.
+ * 
+ * This class may not be directly instantiated, and can only be returned from a
+ * native method. For example,
+ * 
+ * <code>
+ * native JsArray<JavaScriptObject> getNativeArray() /*-{
+ *   return [
+ *     { x: 0, y: 1},
+ *     { x: 2, y: 3},
+ *     { x: 4, y: 5},
+ *   ];
+ * }-* /;
+ * </code>
+ * 
+ * @param <T> the concrete type of object contained in this array
+ */
+public class JsArray<T extends JavaScriptObject> extends JavaScriptObject {
+
+  protected JsArray() {
+  }
+
+  /**
+   * Gets the object at a given index.
+   * 
+   * @param index the index to be retrieved
+   * @return the object at the given index, or <code>null</code> if none
+   *         exists
+   */
+  public final native T get(int index) /*-{
+    return this[index];
+  }-*/;
+
+  /**
+   * Gets the length of the array.
+   * 
+   * @return the array length
+   */
+  public final native int length() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the object value at a given index.
+   * 
+   * If the index is out of bounds, the value will still be set. The array's
+   * length will be updated to encompass the bounds implied by the added object.
+   * 
+   * @param index the index to be set
+   * @param value the object to be stored
+   */
+  public final native void set(int index, T value) /*-{
+    this[index] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/JsArrayBoolean.java b/user/src/com/google/gwt/core/client/JsArrayBoolean.java
new file mode 100644
index 0000000..80bb05e
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/JsArrayBoolean.java
@@ -0,0 +1,70 @@
+/*
+ * 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.core.client;
+
+/**
+ * A simple wrapper around a homogeneous native array of boolean values.
+ * 
+ * This class may not be directly instantiated, and can only be returned from a
+ * native method. For example,
+ * 
+ * <code>
+ * native JsArrayBoolean getNativeArray() /*-{
+ *   return [true, false, true];
+ * }-* /;
+ * </code>
+ */
+public class JsArrayBoolean extends JavaScriptObject {
+
+  protected JsArrayBoolean() {
+  }
+
+  /**
+   * Gets the value at a given index.
+   * 
+   * If an undefined or non-boolean value exists at the given index, a
+   * type-conversion error will occur in hosted mode and unpredictable behavior
+   * may occur in web mode.
+   * 
+   * @param index the index to be retrieved
+   * @return the value at the given index
+   */
+  public final native boolean get(int index) /*-{
+    return this[index];
+  }-*/;
+
+  /**
+   * Gets the length of the array.
+   * 
+   * @return the array length
+   */
+  public final native int length() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the value value at a given index.
+   * 
+   * If the index is out of bounds, the value will still be set. The array's
+   * length will be updated to encompass the bounds implied by the added value.
+   * 
+   * @param index the index to be set
+   * @param value the value to be stored
+   */
+  public final native void set(int index, boolean value) /*-{
+    this[index] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/JsArrayInteger.java b/user/src/com/google/gwt/core/client/JsArrayInteger.java
new file mode 100644
index 0000000..eef3150
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/JsArrayInteger.java
@@ -0,0 +1,71 @@
+/*
+ * 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.core.client;
+
+/**
+ * A simple wrapper around a homogeneous native array of integer values.
+ * 
+ * This class may not be directly instantiated, and can only be returned from a
+ * native method. For example,
+ * 
+ * <code>
+ * native JsArrayInteger getNativeArray() /*-{
+ *   return [1, 2, 3];
+ * }-* /;
+ * </code>
+ */
+public class JsArrayInteger extends JavaScriptObject {
+
+  protected JsArrayInteger() {
+  }
+
+  /**
+   * Gets the value at a given index.
+   * 
+   * If no value exists at the given index, a type-conversion error will occur
+   * in hosted mode and unpredictable behavior may occur in web mode. If the
+   * numeric value returned is non-integral, it will cause a warning in hosted
+   * mode, and may affect the results of mathematical expressions.
+   * 
+   * @param index the index to be retrieved
+   * @return the value at the given index
+   */
+  public final native int get(int index) /*-{
+    return this[index];
+  }-*/;
+
+  /**
+   * Gets the length of the array.
+   * 
+   * @return the array length
+   */
+  public final native int length() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the value value at a given index.
+   * 
+   * If the index is out of bounds, the value will still be set. The array's
+   * length will be updated to encompass the bounds implied by the added value.
+   * 
+   * @param index the index to be set
+   * @param value the value to be stored
+   */
+  public final native void set(int index, int value) /*-{
+    this[index] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/JsArrayNumber.java b/user/src/com/google/gwt/core/client/JsArrayNumber.java
new file mode 100644
index 0000000..c6822d3
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/JsArrayNumber.java
@@ -0,0 +1,73 @@
+/*
+ * 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.core.client;
+
+/**
+ * A simple wrapper around a homogeneous native array of numeric values.
+ * 
+ * All native JavaScript numeric values are implicitly double-precision, so only
+ * double values may be set and retrieved.
+ * 
+ * This class may not be directly instantiated, and can only be returned from a
+ * native method. For example,
+ * 
+ * <code>
+ * native JsArrayNumber getNativeArray() /*-{
+ *   return [1.1, 2.2, 3.3];
+ * }-* /;
+ * </code>
+ */
+public class JsArrayNumber extends JavaScriptObject {
+
+  protected JsArrayNumber() {
+  }
+
+  /**
+   * Gets the value at a given index.
+   * 
+   * If an undefined or non-numeric value exists at the given index, a
+   * type-conversion error will occur in hosted mode and unpredictable behavior
+   * may occur in web mode.
+   * 
+   * @param index the index to be retrieved
+   * @return the value at the given index
+   */
+  public final native double get(int index) /*-{
+    return this[index];
+  }-*/;
+
+  /**
+   * Gets the length of the array.
+   * 
+   * @return the array length
+   */
+  public final native int length() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the value value at a given index.
+   * 
+   * If the index is out of bounds, the value will still be set. The array's
+   * length will be updated to encompass the bounds implied by the added value.
+   * 
+   * @param index the index to be set
+   * @param value the value to be stored
+   */
+  public final native void set(int index, double value) /*-{
+    this[index] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/JsArrayString.java b/user/src/com/google/gwt/core/client/JsArrayString.java
new file mode 100644
index 0000000..d2b1de3
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/JsArrayString.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core.client;
+
+/**
+ * A simple wrapper around a homogeneous native array of string values.
+ * 
+ * This class may not be directly instantiated, and can only be returned from a
+ * native method. For example,
+ * 
+ * <code>
+ * native JsArrayString getNativeArray() /*-{
+ *   return ['foo', 'bar', 'baz'];
+ * }-* /;
+ * </code>
+ */
+public class JsArrayString extends JavaScriptObject {
+
+  protected JsArrayString() {
+  }
+
+  /**
+   * Gets the value at a given index.
+   * 
+   * @param index the index to be retrieved
+   * @return the value at the given index, or <code>null</code> if none exists
+   */
+  public final native String get(int index) /*-{
+    return this[index];
+  }-*/;
+
+  /**
+   * Gets the length of the array.
+   * 
+   * @return the array length
+   */
+  public final native int length() /*-{
+    return this.length;
+  }-*/;
+
+  /**
+   * Sets the value value at a given index.
+   * 
+   * If the index is out of bounds, the value will still be set. The array's
+   * length will be updated to encompass the bounds implied by the added value.
+   * 
+   * @param index the index to be set
+   * @param value the value to be stored
+   */
+  public final native void set(int index, String value) /*-{
+    this[index] = value;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/DOMImplSafari.java b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
index 6d34d26..098d70c 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplSafari.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
@@ -41,9 +41,15 @@
     while (elem) {
       left += elem.offsetLeft;
 
+      // Safari 3 does not include borders with offsetLeft, so we need to add
+      // the borders of the parent manually.
+      var parent = elem.offsetParent;
+      if (parent && $wnd.devicePixelRatio) {
+        left += parseInt($doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-left-width'));
+      }
+
       // Safari bug: a top-level absolutely positioned element includes the
       // body's offset position already.
-      var parent = elem.offsetParent;
       if (parent && (parent.tagName == 'BODY') &&
           (elem.style.position == 'absolute')) {
         break;
@@ -75,9 +81,15 @@
     while (elem) {
       top += elem.offsetTop;
 
+      // Safari 3 does not include borders with offsetTop, so we need to add the
+      // borders of the parent manually.
+      var parent = elem.offsetParent;
+      if (parent && $wnd.devicePixelRatio) {
+        top += parseInt($doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-top-width'));
+      }
+
       // Safari bug: a top-level absolutely positioned element includes the
       // body's offset position already.
-      var parent = elem.offsetParent;
       if (parent && (parent.tagName == 'BODY') &&
           (elem.style.position == 'absolute')) {
         break;
diff --git a/user/src/com/google/gwt/junit/GWTDummyBridge.java b/user/src/com/google/gwt/junit/GWTDummyBridge.java
new file mode 100644
index 0000000..efcef3d
--- /dev/null
+++ b/user/src/com/google/gwt/junit/GWTDummyBridge.java
@@ -0,0 +1,60 @@
+/*
+ * 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.junit;
+
+import com.google.gwt.core.client.GWTBridge;
+import com.google.gwt.dev.About;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A dummy implementation of {@link GWTBridge}, which instantiates nothing.
+ * 
+ * @see GWTMockUtilities
+ */
+class GWTDummyBridge extends GWTBridge {
+  private static final Logger logger = Logger.getLogger(GWTDummyBridge.class.getName());
+
+  /**
+   * @return null
+   */
+  public <T> T create(Class<?> classLiteral) {
+    return null;
+  }
+
+  /**
+   * @return the current version of GWT ({@link About#GWT_VERSION_NUM})
+   */
+  public String getVersion() {
+    return About.GWT_VERSION_NUM;
+  }
+
+  /**
+   * @return false
+   */
+  public boolean isClient() {
+    return false;
+  }
+
+  /**
+   * Logs the message and throwable to the standard logger, with level {@link
+   * Level#SEVERE}.
+   */
+  public void log(String message, Throwable e) {
+    logger.log(Level.SEVERE, message, e);
+  }
+}
diff --git a/user/src/com/google/gwt/junit/GWTMockUtilities.java b/user/src/com/google/gwt/junit/GWTMockUtilities.java
new file mode 100644
index 0000000..b9fe106
--- /dev/null
+++ b/user/src/com/google/gwt/junit/GWTMockUtilities.java
@@ -0,0 +1,99 @@
+/*
+ * 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.junit;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.GWTBridge;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Defangs {@link GWT#create(Class)} to allow unit tests to mock out Widgets and
+ * other UIObjects.
+ */
+public class GWTMockUtilities {
+
+  /**
+   * Replace the normal GWT.create() behavior with a method that returns null
+   * instead of throwing a runtime exception. This is to allow JUnit tests to
+   * mock classes that make GWT.create() calls in their static initializers.
+   * This is not for use with GWTTestCase, and is not for use testing widgets
+   * themselves. Rather, it is to allow pure java unit tests of classes that
+   * need to manipulate widgets.
+   * 
+   * <p>
+   * <b>NOTE:</b> Be sure to call {@link #restore} in your tearDown
+   * method, to avoid confusing downstream tests.
+   * 
+   * <p>
+   * Sample use:
+   * 
+   * <pre>&#64;Override
+   * public void setUp() throws Exception {
+   *   super.setUp();
+   *   GWTMockUtilities.disarm();
+   * }
+   *
+   * &#64;Override
+   * public void tearDown() {
+   *   GWTMockUtilities.restore();
+   * }
+   * 
+   * public void testSomething() {
+   *   MyWidget mock = EasyMock.createMock(MyWidget.class);
+   *   mock.setText("expected text");
+   *   mock.replay();
+   *   
+   *   StatusController controller = new StatusController(mock);
+   *   controller.setStatus("expected text");
+   *   
+   *   EasyMock.verify(mock);
+   * }
+   * </pre>
+   */
+  public static void disarm(/* TearDownAccepter test */) {
+    GWTBridge bridge = new GWTDummyBridge();
+    setGwtBridge(bridge);
+  }
+
+  public static void restore() {
+    setGwtBridge(null);
+  }
+
+  /**
+   * Install the given instance of {@link GWTBridge}, allowing it to override
+   * the behavior of calls to {@link GWT#create(Class)}.
+   */
+  private static void setGwtBridge(GWTBridge bridge) {
+    Class<GWT> gwtClass = GWT.class;
+    Class<?>[] paramTypes = new Class[] {GWTBridge.class};
+    Method setBridgeMethod = null;
+    try {
+      setBridgeMethod = gwtClass.getDeclaredMethod("setBridge", paramTypes);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException(e);
+    }
+    setBridgeMethod.setAccessible(true);
+    try {
+      setBridgeMethod.invoke(gwtClass, new Object[] {bridge});
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException(e);
+    } catch (InvocationTargetException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/junit/JUnitMessageQueue.java b/user/src/com/google/gwt/junit/JUnitMessageQueue.java
index 7466b3c..373d004 100644
--- a/user/src/com/google/gwt/junit/JUnitMessageQueue.java
+++ b/user/src/com/google/gwt/junit/JUnitMessageQueue.java
@@ -149,6 +149,14 @@
       }
       assert (results != null);
       ClientStatus clientStatus = clientStatuses.get(clientId);
+      /*
+       * Unknown client, but valid testInfo; this can happen if the client's
+       * module fails to load.
+       */
+      if (clientStatus == null) {
+        clientStatus = new ClientStatus(clientId);
+        clientStatuses.put(clientId, clientStatus);
+      }
       clientStatus.currentTestResults = results;
       clientStatusesLock.notifyAll();
     }
diff --git a/user/src/com/google/gwt/junit/public/junit.html b/user/src/com/google/gwt/junit/public/junit.html
index ebefbe4..dd33eec 100644
--- a/user/src/com/google/gwt/junit/public/junit.html
+++ b/user/src/com/google/gwt/junit/public/junit.html
@@ -40,7 +40,7 @@
 function junitError(msg) {
   var xmlHttpRequest = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
   xmlHttpRequest.open('POST', 'junithost/loadError', true);
-  xmlHttpRequest.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
+  xmlHttpRequest.setRequestHeader('Content-Type', 'text/x-gwt-rpc; charset=utf-8');
   xmlHttpRequest.send(msg);
 }
 
diff --git a/user/src/com/google/gwt/junit/rebind/GWTRunnerGenerator.java b/user/src/com/google/gwt/junit/rebind/GWTRunnerGenerator.java
index a523d85..19a2d59 100644
--- a/user/src/com/google/gwt/junit/rebind/GWTRunnerGenerator.java
+++ b/user/src/com/google/gwt/junit/rebind/GWTRunnerGenerator.java
@@ -24,7 +24,6 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.junit.client.impl.GWTRunner;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
@@ -90,22 +89,18 @@
         generatedClass, GWT_RUNNER_NAME);
 
     if (sourceWriter != null) {
-      PerfLogger.start("GWTRunnerGenerator");
       JClassType[] allTestTypes = getAllTestTypes(context.getTypeOracle());
       Set<String> testClasses = getTestTypesForModule(logger, moduleName,
           allTestTypes);
       writeCreateNewTestCaseMethod(testClasses, sourceWriter);
       sourceWriter.commit(logger);
-      PerfLogger.end();
     }
 
     return qualifiedStubClassName;
   }
 
   private JClassType[] getAllTestTypes(TypeOracle typeOracle) {
-    PerfLogger.start("GWTRunnerGenerator.getAllTestTypes");
     JClassType gwtTestType = typeOracle.findType(GWTTestCase.class.getName());
-    PerfLogger.end();
     if (gwtTestType != null) {
       return gwtTestType.getSubtypes();
     } else {
@@ -130,7 +125,6 @@
 
   private Set<String> getTestTypesForModule(TreeLogger logger,
       String moduleName, JClassType[] allTestTypes) {
-    PerfLogger.start("GWTRunnerGenerator.getTestTypesForModule");
     // Must use sorted set to prevent nondeterminism.
     Set<String> testClasses = new TreeSet<String>();
     for (JClassType classType : allTestTypes) {
@@ -158,13 +152,11 @@
       }
       testClasses.add(className);
     }
-    PerfLogger.end();
     return testClasses;
   }
 
   private void writeCreateNewTestCaseMethod(Set<String> testClasses,
       SourceWriter sw) {
-    PerfLogger.start("GWTRunnerGenerator.writeCreateNewTestCaseMethod");
     sw.println();
     sw.println("protected final GWTTestCase createNewTestCase(String testClass) {");
     sw.indent();
@@ -183,6 +175,5 @@
     sw.println("return null;");
     sw.outdent();
     sw.println("}");
-    PerfLogger.end();
   }
 }
diff --git a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
index 5bc0b1b..eb7dbbd 100644
--- a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
+++ b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
@@ -81,7 +81,8 @@
   }
 
   public TestInfo getFirstMethod() throws TimeoutException {
-    return getHost().getNextTestInfo(getClientId(), TIME_TO_WAIT_FOR_TESTNAME);
+    return getHost().getNextTestInfo(getClientId(getThreadLocalRequest()),
+        TIME_TO_WAIT_FOR_TESTNAME);
   }
 
   public TestInfo reportResultsAndGetNextMethod(TestInfo testInfo,
@@ -90,7 +91,7 @@
     ExceptionWrapper ew = result.getExceptionWrapper();
     result.setException(deserialize(ew));
     JUnitMessageQueue host = getHost();
-    String clientId = getClientId();
+    String clientId = getClientId(getThreadLocalRequest());
     host.reportResults(clientId, testInfo, result);
     return host.getNextTestInfo(clientId, TIME_TO_WAIT_FOR_TESTNAME);
   }
@@ -104,7 +105,7 @@
       JUnitResult result = new JUnitResult();
       initResult(request, result);
       result.setException(new JUnitFatalLaunchException(requestPayload));
-      getHost().reportResults(getClientId(), null, result);
+      getHost().reportResults(getClientId(request), null, result);
     } else {
       super.service(request, response);
     }
@@ -219,8 +220,7 @@
   /**
    * Returns a "client id" for the current request.
    */
-  private String getClientId() {
-    HttpServletRequest request = getThreadLocalRequest();
+  private String getClientId(HttpServletRequest request) {
     String machine = request.getRemoteHost();
     String agent = request.getHeader("User-Agent");
     return machine + " / " + agent;
diff --git a/user/src/com/google/gwt/user/Focus.gwt.xml b/user/src/com/google/gwt/user/Focus.gwt.xml
index af75aa3..9c591a8 100644
--- a/user/src/com/google/gwt/user/Focus.gwt.xml
+++ b/user/src/com/google/gwt/user/Focus.gwt.xml
@@ -20,12 +20,11 @@
 	<inherits name="com.google.gwt.core.Core"/>
 	<inherits name="com.google.gwt.user.UserAgent"/>
 
-	<!-- old Mozilla, and Opera need a different implementation -->
+	<!-- old Mozilla needs a different implementation -->
 	<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplOld">
 		<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
 		<any>
-		    <when-property-is name="user.agent" value="gecko"/>		      
-			<when-property-is name="user.agent" value="opera"/>
+		    <when-property-is name="user.agent" value="gecko"/>
  	    </any>
 	</replace-with>
 
diff --git a/user/src/com/google/gwt/user/History.gwt.xml b/user/src/com/google/gwt/user/History.gwt.xml
index 7f662cc..3637cad 100644
--- a/user/src/com/google/gwt/user/History.gwt.xml
+++ b/user/src/com/google/gwt/user/History.gwt.xml
@@ -46,4 +46,10 @@
 		<when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
 		<when-property-is name="user.agent" value="safari"/>
 	</replace-with>
+
+  <!-- Opera has yet another history implementation. -->
+  <replace-with class="com.google.gwt.user.client.impl.HistoryImplOpera">
+    <when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
+    <when-property-is name="user.agent" value="opera"/>
+  </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 2d8e3fc..59e351d 100644
--- a/user/src/com/google/gwt/user/client/DOM.java
+++ b/user/src/com/google/gwt/user/client/DOM.java
@@ -403,6 +403,8 @@
    * @return <code>true</code> if ALT was depressed when the event occurred
    */
   public static boolean eventGetAltKey(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK
+        | Event.KEYEVENTS | Event.ONCONTEXTMENU, "altKey");
     return impl.eventGetAltKey(evt);
   }
 
@@ -414,6 +416,7 @@
    *         {@link Event#BUTTON_MIDDLE}, and {@link Event#BUTTON_RIGHT}
    */
   public static int eventGetButton(Event evt) {
+    assertEventType(evt, Event.ONMOUSEDOWN | Event.ONMOUSEUP, "button");
     return impl.eventGetButton(evt);
   }
 
@@ -424,6 +427,8 @@
    * @return the mouse x-position
    */
   public static int eventGetClientX(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK
+        | Event.ONMOUSEWHEEL | Event.ONCONTEXTMENU, "clientX");
     return impl.eventGetClientX(evt);
   }
 
@@ -434,6 +439,8 @@
    * @return the mouse y-position
    */
   public static int eventGetClientY(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK
+        | Event.ONMOUSEWHEEL | Event.ONCONTEXTMENU, "clientY");
     return impl.eventGetClientY(evt);
   }
 
@@ -444,6 +451,8 @@
    * @return <code>true</code> if CTRL was depressed when the event occurred
    */
   public static boolean eventGetCtrlKey(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK
+        | Event.KEYEVENTS | Event.ONCONTEXTMENU, "ctrlKey");
     return impl.eventGetCtrlKey(evt);
   }
 
@@ -478,6 +487,7 @@
    * @return the element from which the mouse pointer was moved
    */
   public static Element eventGetFromElement(Event evt) {
+    assertEventType(evt, Event.ONMOUSEOVER | Event.ONMOUSEOUT, "fromElement");
     return impl.eventGetFromElement(evt);
   }
 
@@ -495,6 +505,7 @@
    * @see com.google.gwt.user.client.ui.KeyboardListener
    */
   public static int eventGetKeyCode(Event evt) {
+    assertEventType(evt, Event.KEYEVENTS, "keyCode");
     return impl.eventGetKeyCode(evt);
   }
 
@@ -505,6 +516,8 @@
    * @return <code>true</code> if META was depressed when the event occurred
    */
   public static boolean eventGetMetaKey(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK  | Event.ONDBLCLICK
+        | Event.KEYEVENTS | Event.ONCONTEXTMENU, "metaKey");
     return impl.eventGetMetaKey(evt);
   }
 
@@ -524,16 +537,18 @@
    * @return The velocity of the mouse wheel.
    */
   public static int eventGetMouseWheelVelocityY(Event evt) {
+    assertEventType(evt, Event.ONMOUSEWHEEL, "mouseWheelVelocityY");
     return impl.eventGetMouseWheelVelocityY(evt);
   }
 
   /**
-   * Gets the key-repeat state of this event.
+   * Gets the key-repeat state of this event.  Only IE supports this attribute.
    * 
    * @param evt the event to be tested
    * @return <code>true</code> if this key event was an auto-repeat
    */
   public static boolean eventGetRepeat(Event evt) {
+    assertEventType(evt, Event.ONKEYDOWN, "repeat");
     return impl.eventGetRepeat(evt);
   }
 
@@ -544,6 +559,8 @@
    * @return the mouse x-position
    */
   public static int eventGetScreenX(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONMOUSEWHEEL | Event.ONCLICK
+        | Event.ONDBLCLICK | Event.ONCONTEXTMENU, "screenX");
     return impl.eventGetScreenX(evt);
   }
 
@@ -554,6 +571,8 @@
    * @return the mouse y-position
    */
   public static int eventGetScreenY(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONMOUSEWHEEL | Event.ONCLICK
+        | Event.ONDBLCLICK | Event.ONCONTEXTMENU, "screenY");
     return impl.eventGetScreenY(evt);
   }
 
@@ -564,6 +583,8 @@
    * @return <code>true</code> if shift was depressed when the event occurred
    */
   public static boolean eventGetShiftKey(Event evt) {
+    assertEventType(evt, Event.MOUSEEVENTS | Event.ONCLICK | Event.ONDBLCLICK
+        | Event.KEYEVENTS | Event.ONCONTEXTMENU, "shiftKey");
     return impl.eventGetShiftKey(evt);
   }
 
@@ -585,6 +606,7 @@
    * @return the element to which the mouse pointer was moved
    */
   public static Element eventGetToElement(Event evt) {
+    assertEventType(evt, Event.ONMOUSEOVER | Event.ONMOUSEOUT, "toElement");
     return impl.eventGetToElement(evt);
   }
 
@@ -624,6 +646,7 @@
    * @param key the new key code
    */
   public static void eventSetKeyCode(Event evt, char key) {
+    assertEventType(evt, Event.KEYEVENTS, "keyCode");
     impl.eventSetKeyCode(evt, key);
   }
 
@@ -1281,6 +1304,20 @@
     return ret;
   }
 
+  /**
+   * Assert that the event is of the given event type, as defined by the
+   * {@link Event} class.
+   * 
+   * @param evt the {@link Event} to check
+   * @param evtType the event type that is expected
+   * @param attr the attribute that should be defined by the event
+   */
+  private static void assertEventType(Event evt, int evtType, String attr) {
+    assert (eventGetType(evt) & evtType) != 0 :
+      "attribute '" + attr + "' not defined for event type '"
+      + eventGetTypeString(evt) + "'";
+  }
+
   private static void dispatchEventAndCatch(Event evt, Element elem,
       EventListener listener, UncaughtExceptionHandler handler) {
     try {
diff --git a/user/src/com/google/gwt/user/client/Element.java b/user/src/com/google/gwt/user/client/Element.java
index 54a5d33..a8b8ca0 100644
--- a/user/src/com/google/gwt/user/client/Element.java
+++ b/user/src/com/google/gwt/user/client/Element.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -24,7 +24,7 @@
  * typically done by calling methods in the
  * {@link com.google.gwt.user.client.DOM} class.
  */
-public final class Element extends com.google.gwt.dom.client.Element {
+public class Element extends com.google.gwt.dom.client.Element {
 
   /**
    * Not directly instantiable. Subclasses should also define a protected no-arg
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index f387190..c3c388a 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -26,7 +26,7 @@
  * from, and can be accessed in JavaScript code as expected. This is typically
  * done by calling methods in the {@link com.google.gwt.user.client.DOM} class.
  */
-public final class Event extends JavaScriptObject {
+public class Event extends JavaScriptObject {
 
   /**
    * The left mouse button (used in {@link DOM#eventGetButton(Event)}).
@@ -135,6 +135,13 @@
   public static final int ONSCROLL = 0x04000;
 
   /**
+   * Fired when the user requests an element's context menu (usually by right-clicking).
+   * 
+   * Note that not all browsers will fire this event (notably Opera, as of 9.5).
+   */
+  public static final int ONCONTEXTMENU = 0x40000;
+
+  /**
    * A bit-mask covering both focus events (focus and blur).
    */
   public static final int FOCUSEVENTS = ONFOCUS | ONBLUR;
@@ -152,14 +159,27 @@
       | ONMOUSEOVER | ONMOUSEOUT;
 
   /**
-   * Error code returned by DOM.getEventXXX methods when the actual integer
-   * value is undefined. For example, DOM.getEventKeyCode returns UNDEFINED for
-   * some non-keyboard events.
+   * Value returned by DOM.getEventXXX methods when the actual integer value is
+   * undefined. For example, DOM.getEventKeyCode returns UNDEFINED for
+   * non-keyboard events.
    * 
-   * For some events, some browsers return undefined while others return data
-   * for certain events.
+   * If assertions are enabled in hosted mode (using the -ea or
+   * -enableassertion compiler flag), DOM.getEventXXX will throw assertion
+   * errors instead of returning UNDEFINED.  The assertions in each
+   * DOM.getEventXXX ensure that the attribute you are retrieving is defined for
+   * all supported browsers.
+   * 
+   * If you disable assertions, some events in some browsers will return
+   * UNDEFINED.  However, other browsers may return meaningless data instead of
+   * UNDEFINED if the attribute does not have meaning in the context of the
+   * Event.  In addition, some methods might return the value 0, which equals
+   * UNDEFINED, when in fact 0 is a valid return type (for example, clientX 
+   * could be 0). As a result, it is NOT safe to rely on the return type of
+   * DOM.getEventXXX methods when assertions are disabled, because the return
+   * values may not have meaning in the context of the event.
    */
-  public static final int UNDEFINED = -1;
+  @Deprecated
+  public static final int UNDEFINED = 0;
 
   /**
    * Gets the current event that is being fired. The current event is only
diff --git a/user/src/com/google/gwt/user/client/Window.java b/user/src/com/google/gwt/user/client/Window.java
index 160b633..aadbc63 100644
--- a/user/src/com/google/gwt/user/client/Window.java
+++ b/user/src/com/google/gwt/user/client/Window.java
@@ -21,6 +21,7 @@
 import com.google.gwt.user.client.impl.WindowImpl;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -33,12 +34,13 @@
   /**
    * This class provides access to the browser's location's object. The location
    * object contains information about the current URL and methods to manipulate
-   * it. <code>Location</code> is a very simple wrapper, so not all browser quirks are hidden from
-   * the user.
+   * it. <code>Location</code> is a very simple wrapper, so not all browser
+   * quirks are hidden from the user.
    * 
    */
   public static class Location {
     private static Map<String, String> paramMap;
+    private static Map<String, String> unmodifiableParamMap;
 
     /**
      * Assigns the window to a new URL. All GWT state will be lost.
@@ -93,7 +95,24 @@
      * @return the value of the URL's parameter
      */
     public static String getParameter(String name) {
-      return ensureParameterMap().get(name);
+      ensureParameterMap();
+      return paramMap.get(name);
+    }
+
+    /**
+     * Returns a Map of the URL query parameters for the host page; since
+     * changing the map would not change the window's location, the map returned
+     * is immutable.
+     * 
+     * @return a map from URL query parameter names to values
+     */
+    public static Map<String, String> getParameterMap() {
+      ensureParameterMap();
+
+      if (unmodifiableParamMap == null) {
+        unmodifiableParamMap = Collections.unmodifiableMap(paramMap);
+      }
+      return unmodifiableParamMap;
     }
 
     /**
@@ -149,7 +168,7 @@
       $wnd.location.replace(newURL);
     }-*/;
 
-    private static Map<String, String> ensureParameterMap() {
+    private static void ensureParameterMap() {
       if (paramMap == null) {
         paramMap = new HashMap<String, String>();
         String queryString = getQueryString();
@@ -165,7 +184,6 @@
           }
         }
       }
-      return paramMap;
     }
 
     private Location() {
@@ -470,7 +488,7 @@
   }-*/;
 
   private static void maybeInitializeHandlers() {
-    if (!handlersAreInitialized) {
+    if (GWT.isClient() && !handlersAreInitialized) {
       init();
       handlersAreInitialized = true;
     }
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 20c7f68..b9d1a5b 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -54,15 +54,15 @@
    }-*/;
 
   public native int eventGetButton(Event evt) /*-{
-    return evt.button || -1;
+    return evt.button || 0;
    }-*/;
 
   public native int eventGetClientX(Event evt) /*-{
-    return evt.clientX || -1;
+    return evt.clientX || 0;
    }-*/;
 
   public native int eventGetClientY(Event evt) /*-{
-    return evt.clientY || -1;
+    return evt.clientY || 0;
    }-*/;
 
   public native boolean eventGetCtrlKey(Event evt) /*-{
@@ -79,7 +79,7 @@
     // 'which' gives the right key value, except when it doesn't -- in which
     // case, keyCode gives the right value on all browsers.
     // If all else fails, return an error code
-    return evt.which || evt.keyCode || -1;
+    return evt.which || evt.keyCode || 0;
    }-*/;
 
   public native boolean eventGetMetaKey(Event evt) /*-{
@@ -93,11 +93,11 @@
    }-*/;
 
   public native int eventGetScreenX(Event evt) /*-{
-    return evt.screenX || -1;
+    return evt.screenX || 0;
    }-*/;
 
   public native int eventGetScreenY(Event evt) /*-{
-    return evt.screenY || -1;
+    return evt.screenY || 0;
    }-*/;
 
   public native boolean eventGetShiftKey(Event evt) /*-{
@@ -133,6 +133,7 @@
         case "error": return 0x10000;
         case "mousewheel": return 0x20000;
         case "DOMMouseScroll": return 0x20000;
+        case "contextmenu": return 0x40000;
       }
     }-*/;
 
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
index 80f038d..f5df1e6 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
@@ -58,7 +58,7 @@
 
   @Override
   public native int eventGetMouseWheelVelocityY(Event evt) /*-{
-    return Math.round(-evt.wheelDelta / 40) || -1;
+    return Math.round(-evt.wheelDelta / 40) || 0;
   }-*/;
 
   @Override
@@ -160,6 +160,7 @@
     $doc.body.attachEvent('onfocus', bodyDispatcher);
     $doc.body.attachEvent('onblur', bodyDispatcher);
     $doc.body.attachEvent('ondblclick', bodyDblClickDispatcher);
+    $doc.body.attachEvent('oncontextmenu', bodyDispatcher);
   }-*/;
 
   @Override
@@ -239,5 +240,7 @@
         @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
         @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
+    if (chMask & 0x40000) elem.oncontextmenu = (bits & 0x40000) ? 
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java b/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
index c7b7734..6c5c0df 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
@@ -24,8 +24,18 @@
 class DOMImplMozilla extends DOMImplStandard {
 
   @Override
+  public native int eventGetClientX(Event evt) /*-{
+    return evt.clientX - $doc.getBoxObjectFor($doc.getElementsByTagName('html')[0]).x || 0;
+  }-*/;
+
+  @Override
+  public native int eventGetClientY(Event evt) /*-{
+    return evt.clientY - $doc.getBoxObjectFor($doc.getElementsByTagName('html')[0]).y || 0;
+  }-*/;
+
+  @Override
   public native int eventGetMouseWheelVelocityY(Event evt) /*-{
-    return evt.detail || -1;
+    return evt.detail || 0;
   }-*/;
 
   @Override
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java b/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
index 9cb970a..d94dbc8 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
@@ -25,7 +25,7 @@
 
   @Override
   public native int eventGetMouseWheelVelocityY(Event evt) /*-{
-    return evt.detail * 4 || -1;
+    return evt.detail * 4 || 0;
   }-*/;
 
   /**
@@ -71,5 +71,7 @@
         @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     elem.onmousewheel  = (bits & 0x20000) ?
         @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
+    elem.oncontextmenu = (bits & 0x40000) ?
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
index bbaf894..bdd5f10 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
@@ -25,17 +25,37 @@
   @Override
   public native int eventGetClientX(Event evt) /*-{
     // In Safari2: clientX is wrong and pageX is returned instead.
-    return evt.pageX - $doc.body.scrollLeft || -1;
+    if ($wnd.devicePixelRatio) {
+      // $wnd.devicePixelRatio identifies Safari 3 from Safari 2
+      return evt.pageX - $doc.body.scrollLeft || 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(Event evt) /*-{
     // In Safari2: clientY is wrong and pageY is returned instead.
-    return evt.pageY - $doc.body.scrollTop || -1;
+    if ($wnd.devicePixelRatio) {
+      // $wnd.devicePixelRatio identifies Safari 3 from Safari 2
+      return evt.pageY - $doc.body.scrollTop || 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(Event evt) /*-{
-    return Math.round(-evt.wheelDelta / 40) || -1;
+    return Math.round(-evt.wheelDelta / 40) || 0;
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
index 31d7a4d..ba0cf6c 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
@@ -48,7 +48,7 @@
     } else if (button == 3) {
       return 2;
     }
-    return button || -1;
+    return button || 0;
   }-*/;
 
   @Override
@@ -256,6 +256,8 @@
         @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
         @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
+    if (chMask & 0x40000) elem.oncontextmenu = (bits & 0x40000) ? 
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
   }-*/;
 
   private native void releaseCaptureImpl(Element elem) /*-{
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplOpera.java b/user/src/com/google/gwt/user/client/impl/HistoryImplOpera.java
new file mode 100644
index 0000000..57ddb0b
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplOpera.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * History implementation for Opera.
+ */
+class HistoryImplOpera extends HistoryImplStandard {
+
+  @Override
+  public native void newItem(String historyToken) /*-{
+    historyToken = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken || "");
+
+    // Opera adds a new stack item each time location.hash is set. All other
+    // browsers check whether the value actually changes, which we duplicate.
+    var prevHistoryToken = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)($wnd.__gwt_historyToken || "");
+    if (prevHistoryToken != historyToken) {
+      $wnd.location.hash = historyToken;
+    }
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java b/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java
index e5fa7e6..7278862 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java
@@ -42,7 +42,7 @@
         @com.google.gwt.user.client.impl.HistoryImpl::onHistoryChanged(Ljava/lang/String;)(token);
       }
 
-      $wnd.setTimeout('__checkHistory()', 250);
+      $wnd.setTimeout($wnd.__checkHistory, 250);
     };
 
     // Kick off the timer.
diff --git a/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java b/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java
index b828e18..a6e8d0e 100644
--- a/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java
+++ b/user/src/com/google/gwt/user/client/impl/WindowImplOpera.java
@@ -15,17 +15,36 @@
  */
 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 {
-  @Override
-  public native int getClientHeight() /*-{
-    return $doc.body.clientHeight;
+  @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 $doc.body.clientWidth;
-  }-*/;
+    return @com.google.gwt.user.client.impl.WindowImplOpera::getBodyElement()().clientWidth;
+  }-*/; 
 }
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index 60c2e0c..c8aaf0a 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -33,6 +33,30 @@
 public abstract class AbstractSerializationStreamWriter extends
     AbstractSerializationStream implements SerializationStreamWriter {
 
+  // Keep synchronized with LongLib
+  private static final double TWO_PWR_16_DBL = 0x10000;
+
+  // Keep synchronized with LongLib
+  private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
+
+  /**
+   * Make an instance equivalent to stringing highBits next to lowBits, where
+   * highBits and lowBits are assumed to be in 32-bit twos-complement notation.
+   * As a result, for any double[] l, the following identity holds:
+   * 
+   * <blockquote> <code>l == makeFromBits(l.highBits(), l.lowBits())</code>
+   * </blockquote>
+   */
+  // Keep synchronized with LongLib
+  protected static double[] makeLongComponents(int highBits, int lowBits) {
+    double high = highBits * TWO_PWR_32_DBL;
+    double low = lowBits;
+    if (lowBits < 0) {
+      low += TWO_PWR_32_DBL;
+    }
+    return new double[] {low, high};
+  }
+
   private int objectCount;
 
   private Map<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index 4146c5d..f9a7a82 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,7 +15,9 @@
  */
 package com.google.gwt.user.client.rpc.impl;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.SerializationException;
 
 /**
@@ -32,6 +34,11 @@
     return array.length;
   }-*/;
 
+  @UnsafeNativeLong
+  private static native long readLong0(double low, double high) /*-{
+    return [low, high];
+  }-*/;
+
   int index;
 
   JavaScriptObject results;
@@ -77,11 +84,11 @@
   }-*/;
 
   public long readLong() {
-    /*
-     * Sent as a string; if we tried to marshal as a number the JSON eval would
-     * lose precision. We use hex due to specially optimized code paths in Long.
-     */
-    return Long.parseLong(readString(), 16);
+    if (GWT.isScript()) {
+      return readLong0(readDouble(), readDouble());
+    } else {
+      return (long) readDouble() + (long) readDouble();
+    }
   }
 
   public native short readShort() /*-{
@@ -103,7 +110,7 @@
   }
 
   @Override
-  protected native String getString(int index) /*-{ 
+  protected native String getString(int index) /*-{
     // index is 1-based
     return index > 0 ? this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::stringTable[index - 1] : null;
   }-*/;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index 85dfc22..df00dc1 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.rpc.impl;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.SerializationException;
 
 import java.util.List;
@@ -31,6 +33,12 @@
     sb.append('\uffff');
   }
 
+  @UnsafeNativeLong
+  // Keep synchronized with LongLib
+  private static native double[] makeLongComponents0(long value) /*-{
+    return value;
+  }-*/;
+
   private StringBuffer encodeBuffer;
 
   private final String moduleBaseURL;
@@ -92,8 +100,22 @@
     return buffer.toString();
   }
 
+  @Override
   public void writeLong(long fieldValue) {
-    append(Long.toString(fieldValue, 16));
+    /*
+     * Client code represents longs internally as an array of two Numbers. In
+     * order to make serialization of longs faster, we'll send the component
+     * parts so that the value can be directly reconstituted on the server.
+     */
+    double[] parts;
+    if (GWT.isScript()) {
+      parts = makeLongComponents0(fieldValue);
+    } else {
+      parts = makeLongComponents((int) (fieldValue >> 32), (int) fieldValue);
+    }
+    assert parts.length == 2;
+    writeDouble(parts[0]);
+    writeDouble(parts[1]);
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/ui/Anchor.java b/user/src/com/google/gwt/user/client/ui/Anchor.java
new file mode 100644
index 0000000..f7669f5
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/Anchor.java
@@ -0,0 +1,265 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.AnchorElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection;
+
+/**
+ * A widget that represents a simple &lt;a&gt; element.
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-Anchor { }</li>
+ * </ul>
+ */
+public class Anchor extends FocusWidget implements HasHorizontalAlignment,
+    HasName, HasText, HasHTML, HasWordWrap, HasDirection {
+
+  /**
+   * Creates an Anchor widget that wraps an existing &lt;div&gt; or &lt;span&gt;
+   * element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Anchor wrap(Element element) {
+    // Assert that the element is of the correct type and is attached.
+    AnchorElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Anchor anchor = new Anchor(element);
+
+    // Mark it attached and remember it for cleanup.
+    anchor.onAttach();
+    RootPanel.detachOnWindowClose(anchor);
+
+    return anchor;
+  }
+
+  private HorizontalAlignmentConstant horzAlign;
+
+  /**
+   * Creates an empty anchor.
+   */
+  public Anchor() {
+    setElement(Document.get().createAnchorElement());
+    setStyleName("gwt-Anchor");
+  }
+
+  /**
+   * Creates an anchor for scripting.
+   * 
+   * The anchor's href is set to <code>javascript:</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   * 
+   * @param text the anchor's text
+   */
+  public Anchor(String text) {
+    this(text, "javascript:");
+  }
+
+  /**
+   * Creates an anchor for scripting.
+   * 
+   * The anchor's href is set to <code>javascript:</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   * 
+   * @param text the anchor's text
+   * @param asHtml <code>true</code> to treat the specified text as html
+   */
+  public Anchor(String text, boolean asHtml) {
+    this(text, asHtml, "javascript:");
+  }
+
+  /**
+   * Creates an anchor with its text and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param href the url to which it will link
+   */
+  public Anchor(String text, boolean asHTML, String href) {
+    this();
+    if (asHTML) {
+      setHTML(text);
+    } else {
+      setText(text);
+    }
+    setHref(href);
+  }
+
+  /**
+   * Creates a source anchor (link to URI).
+   * 
+   * That is, an anchor with an href attribute specifying the destination URI.
+   * 
+   * @param text the anchor's text
+   * @param asHtml asHTML <code>true</code> to treat the specified text as
+   *          html
+   * @param href the url to which it will link
+   * @param target the target frame (e.g. "_blank" to open the link in a new
+   *          window)
+   */
+  public Anchor(String text, boolean asHtml, String href, String target) {
+    this(text, asHtml, href);
+    setTarget(target);
+  }
+
+  /**
+   * Creates an anchor with its text and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param href the url to which it will link
+   */
+  public Anchor(String text, String href) {
+    this();
+    setText(text);
+    setHref(href);
+  }
+
+  /**
+   * Creates a source anchor with a frame target.
+   * 
+   * @param text the anchor's text
+   * @param href the url to which it will link
+   * @param target the target frame (e.g. "_blank" to open the link in a new
+   *          window)
+   */
+  public Anchor(String text, String href, String target) {
+    this(text, false, href, target);
+  }
+
+  private Anchor(Element element) {
+    setElement(element);
+  }
+
+  public Direction getDirection() {
+    return BidiUtils.getDirectionOnElement(getElement());
+  }
+
+  public HorizontalAlignmentConstant getHorizontalAlignment() {
+    return horzAlign;
+  }
+
+  /**
+   * Gets the anchor's href (the url to which it links).
+   * 
+   * @return the anchor's href
+   */
+  public String getHref() {
+    return getAnchorElement().getHref();
+  }
+
+  public String getHTML() {
+    return getElement().getInnerHTML();
+  }
+
+  public String getName() {
+    return getAnchorElement().getName();
+  }
+
+  public int getTabIndex() {
+    return getAnchorElement().getTabIndex();
+  }
+
+  /**
+   * Gets the anchor's target frame (the frame in which navigation will occur
+   * when the link is selected).
+   * 
+   * @return the target frame
+   */
+  public String getTarget() {
+    return getAnchorElement().getTarget();
+  }
+
+  public String getText() {
+    return getElement().getInnerText();
+  }
+
+  public boolean getWordWrap() {
+    return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap");
+  }
+
+  public void setAccessKey(char key) {
+    getAnchorElement().setAccessKey(Character.toString(key));
+  }
+
+  public void setDirection(Direction direction) {
+    BidiUtils.setDirectionOnElement(getElement(), direction);
+  }
+
+  public void setFocus(boolean focused) {
+    if (focused) {
+      getAnchorElement().focus();
+    } else {
+      getAnchorElement().blur();
+    }
+  }
+
+  public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
+    horzAlign = align;
+    getElement().getStyle().setProperty("textAlign", align.getTextAlignString());
+  }
+
+  /**
+   * Sets the anchor's href (the url to which it links).
+   * 
+   * @param href the anchor's href
+   */
+  public void setHref(String href) {
+    getAnchorElement().setHref(href);
+  }
+
+  public void setHTML(String html) {
+    getElement().setInnerHTML(html);
+  }
+
+  public void setName(String name) {
+    getAnchorElement().setName(name);
+  }
+
+  public void setTabIndex(int index) {
+    getAnchorElement().setTabIndex(index);
+  }
+
+  /**
+   * Sets the anchor's target frame (the frame in which navigation will occur
+   * when the link is selected).
+   * 
+   * @param target the target frame
+   */
+  public void setTarget(String target) {
+    getAnchorElement().setTarget(target);
+  }
+
+  public void setText(String text) {
+    getElement().setInnerText(text);
+  }
+
+  public void setWordWrap(boolean wrap) {
+    getElement().getStyle().setProperty("whiteSpace",
+        wrap ? "normal" : "nowrap");
+  }
+
+  private AnchorElement getAnchorElement() {
+    return AnchorElement.as(getElement());
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/Button.java b/user/src/com/google/gwt/user/client/ui/Button.java
index 94e41d8..a29a69b 100644
--- a/user/src/com/google/gwt/user/client/ui/Button.java
+++ b/user/src/com/google/gwt/user/client/ui/Button.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.ButtonElement;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 
@@ -36,9 +38,26 @@
  */
 public class Button extends ButtonBase {
 
-  static native void click(Element button) /*-{
-    button.click();
-  }-*/;
+  /**
+   * Creates a Button widget that wraps an existing &lt;button&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Button wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    ButtonElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Button button = new Button((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    button.onAttach();
+    RootPanel.detachOnWindowClose(button);
+
+    return button;
+  }
 
   static native void adjustType(Element button) /*-{
     // Check before setting this attribute, as not all browsers define it.
@@ -50,6 +69,10 @@
     }
   }-*/;
 
+  static native void click(Element button) /*-{
+    button.click();
+  }-*/;
+
   /**
    * Creates a button with no caption.
    */
@@ -80,6 +103,10 @@
     addClickListener(listener);
   }
 
+  private Button(Element element) {
+    super(element);
+  }
+
   /**
    * Programmatic equivalent of the user clicking the button.
    */
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 14bc01c..5d09e5d 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -19,16 +19,21 @@
 import com.google.gwt.user.client.Element;
 
 /**
- * A standard check box widget (also serves as a base class for
+ * A standard check box widget.
+ * 
+ * This class also serves as a base class for
  * {@link com.google.gwt.user.client.ui.RadioButton}.
+ * 
  * <p>
  * <img class='gallery' src='CheckBox.png'/>
  * </p>
+ * 
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-CheckBox { }</li>
  * <li>.gwt-CheckBox-disabled { Applied when Checkbox is disabled }</li>
  * </ul>
+ * 
  * <p>
  * <h3>Example</h3>
  * {@example com.google.gwt.examples.CheckBoxExample}
@@ -237,7 +242,6 @@
    * @param elem the new input element
    */
   protected void replaceInputElement(Element elem) {
- 
     // Collect information we need to set
     int tabIndex = getTabIndex();
     boolean checked = isChecked();
diff --git a/user/src/com/google/gwt/user/client/ui/FileUpload.java b/user/src/com/google/gwt/user/client/ui/FileUpload.java
index 2ea794b..966fbf3 100644
--- a/user/src/com/google/gwt/user/client/ui/FileUpload.java
+++ b/user/src/com/google/gwt/user/client/ui/FileUpload.java
@@ -15,20 +15,46 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * A widget that wraps the HTML &lt;input type='file'&gt; element. This widget
- * must be used with {@link com.google.gwt.user.client.ui.FormPanel} if it is
- * to be submitted to a server.
+ * must be used with {@link com.google.gwt.user.client.ui.FormPanel} if it is to
+ * be submitted to a server.
  * 
  * <p>
- * <h3>Example</h3> {@example com.google.gwt.examples.FormPanelExample}
+ * <h3>Example</h3>
+ * {@example com.google.gwt.examples.FormPanelExample}
  * </p>
  */
 public class FileUpload extends Widget implements HasName {
 
   /**
+   * Creates a FileUpload widget that wraps an existing &lt;input
+   * type='file'&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static FileUpload wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the right type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("file");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    FileUpload fileUpload = new FileUpload((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    fileUpload.onAttach();
+    RootPanel.detachOnWindowClose(fileUpload);
+
+    return fileUpload;
+  }
+
+  /**
    * Constructs a new file upload widget.
    */
   public FileUpload() {
@@ -37,6 +63,10 @@
     setStyleName("gwt-FileUpload");
   }
 
+  private FileUpload(Element element) {
+    setElement(element);
+  }
+
   /**
    * Gets the filename selected by the user. This property has no mutator, as
    * browser security restrictions preclude setting it.
diff --git a/user/src/com/google/gwt/user/client/ui/FormPanel.java b/user/src/com/google/gwt/user/client/ui/FormPanel.java
index d335822..d873e14 100644
--- a/user/src/com/google/gwt/user/client/ui/FormPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FormPanel.java
@@ -16,8 +16,10 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.Command;
 import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.FormElement;
+import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Element;
@@ -85,6 +87,27 @@
   private static int formId = 0;
   private static FormPanelImpl impl = GWT.create(FormPanelImpl.class);
 
+  /**
+   * Creates a FormPanel that wraps an existing &lt;form&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static FormPanel wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    FormElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    FormPanel formPanel = new FormPanel((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    formPanel.onAttach();
+    RootPanel.detachOnWindowClose(formPanel);
+
+    return formPanel;
+  }
+
   private FormHandlerCollection formHandlers;
   private String frameName;
   private Element iframe;
@@ -152,6 +175,10 @@
     setTarget(target);
   }
 
+  private FormPanel(Element elem) {
+    super(elem);
+  }
+
   public void addFormHandler(FormHandler handler) {
     if (formHandlers == null) {
       formHandlers = new FormHandlerCollection();
diff --git a/user/src/com/google/gwt/user/client/ui/Frame.java b/user/src/com/google/gwt/user/client/ui/Frame.java
index e58bbe7..c8a6069 100644
--- a/user/src/com/google/gwt/user/client/ui/Frame.java
+++ b/user/src/com/google/gwt/user/client/ui/Frame.java
@@ -15,7 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.IFrameElement;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * A widget that wraps an IFRAME element, which can contain an arbitrary web
@@ -35,8 +38,30 @@
  * </p>
  */
 public class Frame extends Widget {
+
   static final String DEFAULT_STYLENAME = "gwt-Frame";
-  
+
+  /**
+   * Creates a Frame widget that wraps an existing &lt;frame&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Frame wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    IFrameElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Frame frame = new Frame((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    frame.onAttach();
+    RootPanel.detachOnWindowClose(frame);
+
+    return frame;
+  }
+
   /**
    * Creates an empty frame.
    */
@@ -55,6 +80,10 @@
     setUrl(url);
   }
 
+  private Frame(Element element) {
+    setElement(element);
+  }
+
   /**
    * Gets the URL of the frame's resource.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/HTML.java b/user/src/com/google/gwt/user/client/ui/HTML.java
index 80edbc2..5cebe0a 100644
--- a/user/src/com/google/gwt/user/client/ui/HTML.java
+++ b/user/src/com/google/gwt/user/client/ui/HTML.java
@@ -15,11 +15,16 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * A widget that can contain arbitrary HTML.
  * 
+ * This widget uses a &lt;div&gt; element, causing it to be displayed with block
+ * layout.
+ * 
  * <p>
  * If you only need a simple label (text, but not HTML), then the
  * {@link com.google.gwt.user.client.ui.Label} widget is more appropriate, as it
@@ -40,6 +45,29 @@
 public class HTML extends Label implements HasHTML {
 
   /**
+   * Creates an HTML widget that wraps an existing &lt;div&gt; or &lt;span&gt;
+   * element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static HTML wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert element.getTagName().equalsIgnoreCase("div")
+        || element.getTagName().equalsIgnoreCase("span");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    HTML html = new HTML((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    html.onAttach();
+    RootPanel.detachOnWindowClose(html);
+
+    return html;
+  }
+
+  /**
    * Creates an empty HTML widget.
    */
   public HTML() {
@@ -69,6 +97,10 @@
     setWordWrap(wordWrap);
   }
 
+  HTML(Element element) {
+    super(element);
+  }
+
   public String getHTML() {
     return DOM.getInnerHTML(getElement());
   }
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 b2e9d03..ca7fa68 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
@@ -89,7 +89,11 @@
   }
 
   /**
-   * Finds an {@link Element element} within this panel by its id. 
+   * Finds an {@link Element element} within this panel by its id.
+   * 
+   * This method uses
+   * {@link com.google.gwt.dom.client.Document#getElementById(String)}, so the
+   * id must still be unique within the document.
    * 
    * @param id the id of the element to be found
    * @return the element with the given id, or <code>null</code> if none is found
diff --git a/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java b/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java
index ab187aa..7518d12 100644
--- a/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java
+++ b/user/src/com/google/gwt/user/client/ui/HasHorizontalAlignment.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.i18n.client.LocaleInfo;
 
 /**
@@ -67,7 +68,8 @@
    * should be aligned to the left.
    */
   HorizontalAlignmentConstant ALIGN_DEFAULT = 
-      (LocaleInfo.getCurrentLocale().isRTL()) ? ALIGN_RIGHT : ALIGN_LEFT;
+      (GWT.isClient() && (LocaleInfo.getCurrentLocale().isRTL()))
+        ? ALIGN_RIGHT : ALIGN_LEFT;
       
   /**
    * Gets the horizontal alignment.
@@ -86,4 +88,4 @@
    *          {@link HasHorizontalAlignment#ALIGN_DEFAULT}).
    */
   void setHorizontalAlignment(HorizontalAlignmentConstant align);
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/gwt/user/client/ui/Hidden.java b/user/src/com/google/gwt/user/client/ui/Hidden.java
index 2c48c71..b667197 100644
--- a/user/src/com/google/gwt/user/client/ui/Hidden.java
+++ b/user/src/com/google/gwt/user/client/ui/Hidden.java
@@ -16,6 +16,8 @@
 
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 
@@ -25,6 +27,28 @@
 public class Hidden extends Widget implements HasName {
 
   /**
+   * Creates a Hidden widget that wraps an existing &lt;input type='hidden'&gt;
+   * element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Hidden wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("hidden");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Hidden hidden = new Hidden((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    hidden.onAttach();
+    RootPanel.detachOnWindowClose(hidden);
+
+    return hidden;
+  }
+
+  /**
    * Constructor for <code>Hidden</code>.
    */
   public Hidden() {
@@ -54,6 +78,10 @@
     setValue(value);
   }
 
+  private Hidden(Element element) {
+    setElement(element);
+  }
+
   /**
    * Gets the default value of the hidden field.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java
index 0031214..a6feb7e 100644
--- a/user/src/com/google/gwt/user/client/ui/Image.java
+++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -15,13 +15,15 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.ImageElement;
+import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.ui.impl.ClippedImageImpl;
-import com.google.gwt.core.client.GWT;
 
 import java.util.HashMap;
 
@@ -221,6 +223,11 @@
    */
   private static class UnclippedState extends State {
 
+    UnclippedState(Element element) {
+      DOM.sinkEvents(element, Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD
+          | Event.ONERROR | Event.ONMOUSEWHEEL);
+    }
+
     UnclippedState(Image image) {
       image.replaceElement(DOM.createImg());
       image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD
@@ -300,6 +307,28 @@
     prefetchImages.put(url, img);
   }
 
+  /**
+   * Creates a Image widget that wraps an existing &lt;img&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Image wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    ImageElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Image image = new Image((Element) element);
+    image.changeState(new UnclippedState((Element) element));
+
+    // Mark it attached and remember it for cleanup.
+    image.onAttach();
+    RootPanel.detachOnWindowClose(image);
+
+    return image;
+  }
+
   private ClickListenerCollection clickListeners;
   private LoadListenerCollection loadListeners;
   private MouseListenerCollection mouseListeners;
@@ -349,6 +378,10 @@
     setStyleName("gwt-Image");
   }
 
+  private Image(Element element) {
+    setElement(element);
+  }
+
   public void addClickListener(ClickListener listener) {
     if (clickListeners == null) {
       clickListeners = new ClickListenerCollection();
diff --git a/user/src/com/google/gwt/user/client/ui/InlineHTML.java b/user/src/com/google/gwt/user/client/ui/InlineHTML.java
new file mode 100644
index 0000000..36c885a
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/InlineHTML.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * A widget that can contain arbitrary HTML.
+ * 
+ * This widget uses a &lt;span&gt; element, causing it to be displayed with
+ * inline layout.
+ * 
+ * <p>
+ * If you only need a simple label (text, but not HTML), then the
+ * {@link com.google.gwt.user.client.ui.Label} widget is more appropriate, as it
+ * disallows the use of HTML, which can lead to potential security issues if not
+ * used properly.
+ * </p>
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-InlineHTML { }</li>
+ * </ul>
+ */
+public class InlineHTML extends HTML {
+
+  /**
+   * Creates an InlineHTML widget that wraps an existing &lt;div&gt; or
+   * &lt;span&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static InlineHTML wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert element.getTagName().equalsIgnoreCase("div")
+        || element.getTagName().equalsIgnoreCase("span");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    InlineHTML html = new InlineHTML((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    html.onAttach();
+    RootPanel.detachOnWindowClose(html);
+
+    return html;
+  }
+
+  /**
+   * Creates an empty HTML widget.
+   */
+  public InlineHTML() {
+    super(DOM.createSpan());
+    setStyleName("gwt-InlineHTML");
+  }
+
+  /**
+   * Creates an HTML widget with the specified HTML contents.
+   * 
+   * @param html the new widget's HTML contents
+   */
+  public InlineHTML(String html) {
+    this();
+    setHTML(html);
+  }
+
+  private InlineHTML(Element element) {
+    super(element);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/InlineLabel.java b/user/src/com/google/gwt/user/client/ui/InlineLabel.java
new file mode 100644
index 0000000..d69affa
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/InlineLabel.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
+ * 
+ * This widget uses a &lt;span&gt; element, causing it to be displayed with
+ * inline layout.
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-InlineLabel { }</li>
+ * </ul>
+ */
+public class InlineLabel extends Label {
+
+  /**
+   * Creates a InlineLabel widget that wraps an existing &lt;div&gt; or
+   * &lt;span&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static InlineLabel wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert element.getTagName().equalsIgnoreCase("div")
+        || element.getTagName().equalsIgnoreCase("span");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    InlineLabel label = new InlineLabel((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    label.onAttach();
+    RootPanel.detachOnWindowClose(label);
+
+    return label;
+  }
+
+  /**
+   * Creates an empty label.
+   */
+  public InlineLabel() {
+    super(DOM.createSpan());
+    setStyleName("gwt-InlineLabel");
+  }
+
+  /**
+   * Creates a label with the specified text.
+   * 
+   * @param text the new label's text
+   */
+  public InlineLabel(String text) {
+    this();
+    setText(text);
+  }
+
+  private InlineLabel(Element element) {
+    super(element);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/Label.java b/user/src/com/google/gwt/user/client/ui/Label.java
index 386ab0a..c985697 100644
--- a/user/src/com/google/gwt/user/client/ui/Label.java
+++ b/user/src/com/google/gwt/user/client/ui/Label.java
@@ -15,15 +15,19 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection;
 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.i18n.client.HasDirection;
-import com.google.gwt.i18n.client.BidiUtils;
 
 /**
  * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
  * 
+ * This widget uses a &lt;div&gt; element, causing it to be displayed with
+ * block layout.
+ * 
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-Label { }</li>
@@ -38,6 +42,29 @@
     SourcesMouseEvents, SourcesMouseWheelEvents, HasHorizontalAlignment,
     HasText, HasWordWrap, HasDirection {
 
+  /**
+   * Creates a Label widget that wraps an existing &lt;div&gt; or &lt;span&gt;
+   * element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static Label wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert element.getTagName().equalsIgnoreCase("div")
+        || element.getTagName().equalsIgnoreCase("span");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    Label label = new Label((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    label.onAttach();
+    RootPanel.detachOnWindowClose(label);
+
+    return label;
+  }
+
   private ClickListenerCollection clickListeners;
   private HorizontalAlignmentConstant horzAlign;
   private MouseListenerCollection mouseListeners;
@@ -52,15 +79,6 @@
   }
 
   /**
-   * This constructor is used to let the HTML constructors avoid work.
-   * 
-   * @param element element
-   */
-  Label(Element element) {
-    setElement(element);
-  }
-
-  /**
    * Creates a label with the specified text.
    * 
    * @param text the new label's text
@@ -81,6 +99,15 @@
     setWordWrap(wordWrap);
   }
 
+  /**
+   * This constructor is used to let the HTML constructors avoid work.
+   * 
+   * @param element element
+   */
+  Label(Element element) {
+    setElement(element);
+  }
+
   public void addClickListener(ClickListener listener) {
     if (clickListeners == null) {
       clickListeners = new ClickListenerCollection();
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 41c27ef..a403499 100644
--- a/user/src/com/google/gwt/user/client/ui/ListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -15,10 +15,12 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.SelectElement;
 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.core.client.GWT;
 
 /**
  * A widget that presents a list of choices to the user, either as a list box or
@@ -148,6 +150,28 @@
 
   private static final int INSERT_AT_END = -1;
   private static final Impl impl = GWT.create(Impl.class);
+
+  /**
+   * Creates a ListBox widget that wraps an existing &lt;select&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static ListBox wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    SelectElement.as(element);
+    assert Document.get().getBody().isOrHasChild(element);
+
+    ListBox listBox = new ListBox((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    listBox.onAttach();
+    RootPanel.detachOnWindowClose(listBox);
+
+    return listBox;
+  }
+
   private ChangeListenerCollection changeListeners;
 
   /**
@@ -168,6 +192,10 @@
     setStyleName("gwt-ListBox");
   }
 
+  private ListBox(Element element) {
+    super(element);
+  }
+
   public void addChangeListener(ChangeListener listener) {
     if (changeListeners == null) {
       changeListeners = new ChangeListenerCollection();
diff --git a/user/src/com/google/gwt/user/client/ui/MultiWordSuggestOracle.java b/user/src/com/google/gwt/user/client/ui/MultiWordSuggestOracle.java
index 777b8a6..a4f91b3 100644
--- a/user/src/com/google/gwt/user/client/ui/MultiWordSuggestOracle.java
+++ b/user/src/com/google/gwt/user/client/ui/MultiWordSuggestOracle.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.rpc.IsSerializable;
 
 import java.util.ArrayList;
@@ -103,7 +104,7 @@
    */
   private static final String NORMALIZE_TO_SINGLE_WHITE_SPACE = "\\s+";
 
-  private static HTML convertMe = new HTML();
+  private static HTML convertMe = GWT.isClient() ? new HTML() : null;
 
   /**
    * Associates substrings with words.
diff --git a/user/src/com/google/gwt/user/client/ui/NamedFrame.java b/user/src/com/google/gwt/user/client/ui/NamedFrame.java
index 2fa73e0..9efe1f7 100644
--- a/user/src/com/google/gwt/user/client/ui/NamedFrame.java
+++ b/user/src/com/google/gwt/user/client/ui/NamedFrame.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -37,7 +38,9 @@
   private static JavaScriptObject PATTERN_NAME;
 
   static {
-    initStatics();
+    if (GWT.isClient()) {
+      initStatics();
+    }
   }
 
   private static native void initStatics() /*-{
diff --git a/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java b/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java
index b670cab..de63755 100644
--- a/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java
+++ b/user/src/com/google/gwt/user/client/ui/PasswordTextBox.java
@@ -15,7 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * A text box that visually masks its input to prevent eavesdropping.
@@ -37,10 +40,36 @@
 public class PasswordTextBox extends TextBox {
 
   /**
+   * Creates a PasswordTextBox widget that wraps an existing &lt;input
+   * type='password'&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static PasswordTextBox wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("password");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    PasswordTextBox textBox = new PasswordTextBox((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    textBox.onAttach();
+    RootPanel.detachOnWindowClose(textBox);
+
+    return textBox;
+  }
+
+  /**
    * Creates an empty password text box.
    */
   public PasswordTextBox() {
     super(DOM.createInputPassword());
     setStyleName("gwt-PasswordTextBox");
   }
+
+  private PasswordTextBox(Element element) {
+    super(element);
+  }
 }
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 8205251..ea02702 100644
--- a/user/src/com/google/gwt/user/client/ui/RootPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/RootPanel.java
@@ -23,7 +23,9 @@
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * The panel to which all other widgets must ultimately be added. RootPanels are
@@ -37,6 +39,21 @@
 public class RootPanel extends AbsolutePanel {
 
   private static HashMap<String, RootPanel> rootPanels = new HashMap<String, RootPanel>();
+  private static List<Widget> widgetsToDetach = new ArrayList<Widget>();
+
+  /**
+   * Adds a widget to the list of widgets to be detached when the page unloads.
+   * 
+   * 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.
+   * 
+   * @param widget the widget to be cleaned up when the page closes
+   */
+  public static void detachOnWindowClose(Widget widget) {
+    widgetsToDetach.add(widget);
+  }
 
   /**
    * Gets the default root panel. This panel wraps body of the browser's
@@ -82,7 +99,6 @@
       
       // If we're in a RTL locale, set the RTL directionality
       // on the entire document.
-      
       if (LocaleInfo.getCurrentLocale().isRTL()) {
         BidiUtils.setDirectionOnElement(getRootElement(), HasDirection.Direction.RTL);
       }      
@@ -94,6 +110,7 @@
       elem = getBodyElement();
     }
     rootPanels.put(id, rp = new RootPanel(elem));
+    widgetsToDetach.add(rp);
     return rp;
   }
 
@@ -115,17 +132,18 @@
   private static native Element getRootElement() /*-{
     return $doc;
   }-*/;
-  
+
   private static void hookWindowClosing() {
     // Catch the window closing event.
     Window.addWindowCloseListener(new WindowCloseListener() {
       public void onWindowClosed() {
-        // When the window is closing, detach all root panels. This will cause
-        // all of their children's event listeners to be unhooked, which will
-        // avoid potential memory leaks.
-        for (RootPanel gwt : rootPanels.values()) {
-          if (gwt.isAttached()) {
-            gwt.onDetach();
+        // When the window is closing, detach all widgets that need to be
+        // cleaned up. This will cause all of their event listeners
+        // to be unhooked, which will avoid potential memory leaks.
+        for (int i = 0; i < widgetsToDetach.size(); ++i) {
+          Widget widget = widgetsToDetach.get(i);
+          if (widget.isAttached()) {
+            widget.onDetach();
           }
         }
       }
diff --git a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
new file mode 100644
index 0000000..d241e1115
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+
+/**
+ * A simple checkbox widget, with no label.
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-SimpleCheckBox { }</li>
+ * <li>.gwt-SimpleCheckBox-disabled { Applied when checkbox is disabled }</li>
+ * </ul>
+ */
+public class SimpleCheckBox extends FocusWidget implements HasName {
+
+  /**
+   * Creates a SimpleCheckBox widget that wraps an existing &lt;input
+   * type='checkbox'&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static SimpleCheckBox wrap(Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("checkbox");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    SimpleCheckBox checkBox = new SimpleCheckBox(element);
+
+    // Mark it attached and remember it for cleanup.
+    checkBox.onAttach();
+    RootPanel.detachOnWindowClose(checkBox);
+
+    return checkBox;
+  }
+
+  /**
+   * Creates a new simple checkbox.
+   */
+  public SimpleCheckBox() {
+    setElement(Document.get().createCheckInputElement());
+    setStyleName("gwt-SimpleCheckBox");
+  }
+
+  SimpleCheckBox(Element element) {
+    setElement(element);
+  }
+
+  public String getName() {
+    return getInputElement().getName();
+  }
+
+  /**
+   * Determines whether this check box is currently checked.
+   * 
+   * @return <code>true</code> if the check box is checked
+   */
+  public boolean isChecked() {
+    String propName = isAttached() ? "checked" : "defaultChecked";
+    return getInputElement().getPropertyBoolean(propName);
+  }
+
+  /**
+   * Checks or unchecks this check box.
+   * 
+   * @param checked <code>true</code> to check the check box
+   */
+  public void setChecked(boolean checked) {
+    getInputElement().setChecked(checked);
+    getInputElement().setDefaultChecked(checked);
+  }
+
+  public void setEnabled(boolean enabled) {
+    super.setEnabled(enabled);
+    if (enabled) {
+      removeStyleDependentName("disabled");
+    } else {
+      addStyleDependentName("disabled");
+    }
+  }
+
+  public void setName(String name) {
+    getInputElement().setName(name);
+  }
+
+  /**
+   * This method is called when a widget is detached from the browser's
+   * document. Overridden because of IE bug that throws away checked state and
+   * in order to clear the event listener off of the <code>inputElem</code>.
+   */
+  @Override
+  protected void onUnload() {
+    setChecked(isChecked());
+  }
+
+  private InputElement getInputElement() {
+    return InputElement.as(getElement());
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java b/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java
new file mode 100644
index 0000000..aac907b
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/SimpleRadioButton.java
@@ -0,0 +1,73 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+
+/**
+ * A simple radio button widget, with no label.
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-SimpleRadioButton { }</li>
+ * <li>.gwt-SimpleRadioButton-disabled { Applied when radio button is disabled }</li>
+ * </ul>
+ */
+public class SimpleRadioButton extends SimpleCheckBox {
+
+  /**
+   * Creates a SimpleRadioButton widget that wraps an existing &lt;input
+   * type='radio'&gt; element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static SimpleRadioButton wrap(Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("radio");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    SimpleRadioButton radioButton = new SimpleRadioButton(element);
+
+    // Mark it attached and remember it for cleanup.
+    radioButton.onAttach();
+    RootPanel.detachOnWindowClose(radioButton);
+
+    return radioButton;
+  }
+
+  /**
+   * Creates a new radio associated with a particular group name. All radio
+   * buttons associated with the same group name belong to a mutually-exclusive
+   * set.
+   * 
+   * Radio buttons are grouped by their name attribute, so changing their name
+   * using the setName() method will also change their associated group.
+   * 
+   * @param name the group name with which to associate the radio button
+   */
+  public SimpleRadioButton(String name) {
+    this(Document.get().createRadioInputElement(name));
+    setStyleName("gwt-SimpleRadioButton");
+  }
+
+  private SimpleRadioButton(Element element) {
+    super(element);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/TabBar.java b/user/src/com/google/gwt/user/client/ui/TabBar.java
index d1e6a94..37d2bd0 100644
--- a/user/src/com/google/gwt/user/client/ui/TabBar.java
+++ b/user/src/com/google/gwt/user/client/ui/TabBar.java
@@ -316,6 +316,42 @@
   }
 
   /**
+   * Sets a tab's contents via HTML.
+   * 
+   * Use care when setting an object's HTML; it is an easy way to expose
+   * script-based security problems. Consider using
+   * {@link #setTabText(int, String)} whenever possible.
+   * 
+   * @param index the index of the tab whose HTML is to be set
+   * @param html the tab new HTML
+   */
+  public void setTabHTML(int index, String html) {
+    assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds";
+
+    ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1);
+    SimplePanel focusablePanel = delPanel.getFocusablePanel();
+    focusablePanel.setWidget(new HTML(html));
+  }
+
+  /**
+   * Sets a tab's text contents.
+   * 
+   * @param index the index of the tab whose text is to be set
+   * @param text the object's new text
+   */
+  public void setTabText(int index, String text) {
+    assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds";
+
+    ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1);
+    SimplePanel focusablePanel = delPanel.getFocusablePanel();
+
+    // It is not safe to check if the current widget is an instanceof Label and
+    // reuse it here because HTML is an instanceof Label.  Leaving an HTML would
+    // throw off the results of getTabHTML(int).
+    focusablePanel.setWidget(new Label(text));
+  }
+
+  /**
    * Create a {@link SimplePanel} that will wrap the contents in a tab.
    * Subclasses can use this method to wrap tabs in decorator panels.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/TextBox.java b/user/src/com/google/gwt/user/client/ui/TextBox.java
index 1108fbf..a3ff602 100644
--- a/user/src/com/google/gwt/user/client/ui/TextBox.java
+++ b/user/src/com/google/gwt/user/client/ui/TextBox.java
@@ -15,10 +15,12 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
-import com.google.gwt.i18n.client.HasDirection;
-import com.google.gwt.i18n.client.BidiUtils;
 
 /**
  * A standard single-line text box.
@@ -41,13 +43,35 @@
 public class TextBox extends TextBoxBase implements HasDirection {
 
   /**
+   * Creates a TextBox widget that wraps an existing &lt;input type='text'&gt;
+   * element.
+   * 
+   * This element must already be attached to the document.
+   * 
+   * @param element the element to be wrapped
+   */
+  public static TextBox wrap(com.google.gwt.dom.client.Element element) {
+    // Assert that the element is of the correct type and is attached.
+    assert InputElement.as(element).getType().equalsIgnoreCase("text");
+    assert Document.get().getBody().isOrHasChild(element);
+
+    TextBox textBox = new TextBox((Element) element);
+
+    // Mark it attached and remember it for cleanup.
+    textBox.onAttach();
+    RootPanel.detachOnWindowClose(textBox);
+
+    return textBox;
+  }
+
+  /**
    * Creates an empty text box.
    */
   public TextBox() {
     super(DOM.createInputText());
     setStyleName("gwt-TextBox");
   }
-  
+
   /**
    * Protected constructor for use by subclasses.
    * @param element element
diff --git a/user/src/com/google/gwt/user/client/ui/TreeItem.java b/user/src/com/google/gwt/user/client/ui/TreeItem.java
index ac71ea3..f68728b 100644
--- a/user/src/com/google/gwt/user/client/ui/TreeItem.java
+++ b/user/src/com/google/gwt/user/client/ui/TreeItem.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.animation.client.Animation;
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -177,29 +178,31 @@
    * Static constructor to set up clonable elements.
    */
   static {
-    // Create the base table element that will be cloned.
-    BASE_INTERNAL_ELEM = DOM.createTable();
-    Element contentElem = DOM.createSpan();
-    Element tbody = DOM.createTBody(), tr = DOM.createTR();
-    Element tdImg = DOM.createTD(), tdContent = DOM.createTD();
-    DOM.appendChild(BASE_INTERNAL_ELEM, tbody);
-    DOM.appendChild(tbody, tr);
-    DOM.appendChild(tr, tdImg);
-    DOM.appendChild(tr, tdContent);
-    DOM.setStyleAttribute(tdImg, "verticalAlign", "middle");
-    DOM.setStyleAttribute(tdContent, "verticalAlign", "middle");
-    DOM.appendChild(tdContent, contentElem);
-    DOM.setStyleAttribute(contentElem, "display", "inline");
-    setStyleName(contentElem, "gwt-TreeItem");
-    DOM.setStyleAttribute(BASE_INTERNAL_ELEM, "whiteSpace", "nowrap");
+    if (GWT.isClient()) {
+      // Create the base table element that will be cloned.
+      BASE_INTERNAL_ELEM = DOM.createTable();
+      Element contentElem = DOM.createSpan();
+      Element tbody = DOM.createTBody(), tr = DOM.createTR();
+      Element tdImg = DOM.createTD(), tdContent = DOM.createTD();
+      DOM.appendChild(BASE_INTERNAL_ELEM, tbody);
+      DOM.appendChild(tbody, tr);
+      DOM.appendChild(tr, tdImg);
+      DOM.appendChild(tr, tdContent);
+      DOM.setStyleAttribute(tdImg, "verticalAlign", "middle");
+      DOM.setStyleAttribute(tdContent, "verticalAlign", "middle");
+      DOM.appendChild(tdContent, contentElem);
+      DOM.setStyleAttribute(contentElem, "display", "inline");
+      setStyleName(contentElem, "gwt-TreeItem");
+      DOM.setStyleAttribute(BASE_INTERNAL_ELEM, "whiteSpace", "nowrap");
 
-    // Create the base element that will be cloned
-    BASE_BARE_ELEM = DOM.createDiv();
+      // Create the base element that will be cloned
+      BASE_BARE_ELEM = DOM.createDiv();
 
-    // Simulates padding from table element.
-    DOM.setStyleAttribute(BASE_BARE_ELEM, "padding", "3px");
-    DOM.appendChild(BASE_BARE_ELEM, contentElem);
-    Accessibility.setRole(contentElem, Accessibility.ROLE_TREEITEM);
+      // Simulates padding from table element.
+      DOM.setStyleAttribute(BASE_BARE_ELEM, "padding", "3px");
+      DOM.appendChild(BASE_BARE_ELEM, contentElem);
+      Accessibility.setRole(contentElem, Accessibility.ROLE_TREEITEM);
+    }
   }
   private ArrayList<TreeItem> children;
   private Element contentElem, childSpanElem, imageHolder;
diff --git a/user/src/com/google/gwt/user/rebind/rpc/JModTypeVisitor.java b/user/src/com/google/gwt/user/rebind/rpc/JModTypeVisitor.java
new file mode 100644
index 0000000..9ebeabe
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/JModTypeVisitor.java
@@ -0,0 +1,154 @@
+/*
+ * 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.rebind.rpc;
+
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JWildcardType;
+
+/**
+ * <p>
+ * This visitor supports transforming interior parts of a type. Subclasses
+ * should override the appropriate <code>endVisit</code> methods and, when
+ * such a method wants to replace its argument, assign the replacement to the
+ * field {@link #replacement}.
+ * </p>
+ * 
+ * <p>
+ * Any specified replacements must be sensible no matter what context they might
+ * be used in. Specifically:
+ * </p>
+ * <ul>
+ * <li>Primitive types may only be replaced by primitive types.
+ * <li>Reference types may only be replaced by reference types.
+ * <li>Generic types may only be replaced by generic types, and the replacement
+ * should have the same number of type parameters as the original.
+ * </ul>
+ */
+class JModTypeVisitor extends JTypeVisitor {
+  /**
+   * A subclass's <code>endVisit</code> method can indicate that it wants to
+   * replace its argument by assigning the replacement to this field.
+   */
+  protected JType replacement;
+
+  @Override
+  public void accept(JType type) {
+    replacement = type;
+    acceptChildren(type);
+    endVisit(replacement);
+  }
+
+  public JClassType transform(JClassType type) {
+    // transforming a class type should give a class type back
+    return (JClassType) transform((JType) type);
+  }
+
+  public JGenericType transform(JGenericType type) {
+    // transforming a generic type should give a generic type back
+    return (JGenericType) transform((JType) type);
+  }
+
+  public JPrimitiveType transform(JPrimitiveType type) {
+    // transforming a primitive type should give a primitive type back
+    return (JPrimitiveType) transform((JType) type);
+  }
+
+  public JType transform(JType type) {
+    accept(type);
+    return replacement;
+  }
+
+  /**
+   * Do the same thing as {@link JTypeVisitor#acceptChildren(JType)}, but if
+   * any children types are replaced, reconstruct a version of <code>type</code>
+   * that has those corresponding replacements made and assign the new type to
+   * {@link #replacement}.
+   */
+  @Override
+  protected void acceptChildren(JType type) {
+    JArrayType typeArray = type.isArray();
+    if (typeArray != null) {
+      JType oldComponentType = typeArray.getComponentType();
+      JType newComponentType = transform(oldComponentType);
+
+      if (oldComponentType == newComponentType) {
+        replacement = type;
+      } else {
+        replacement = typeArray.getOracle().getArrayType(newComponentType);
+      }
+    }
+
+    JParameterizedType typeParameterized = type.isParameterized();
+    if (typeParameterized != null) {
+      JGenericType oldBaseType = typeParameterized.getBaseType();
+      JGenericType newBaseType = transform(oldBaseType);
+
+      JClassType oldEnclosingType = typeParameterized.getEnclosingType();
+      JClassType newEnclosingType = oldEnclosingType == null ? null
+          : transform(oldEnclosingType);
+
+      JClassType[] oldTypeArgs = typeParameterized.getTypeArgs();
+      JClassType[] newTypeArgs = new JClassType[oldTypeArgs.length];
+      boolean argsAllSame = true;
+      for (int i = 0; i < oldTypeArgs.length; i++) {
+        newTypeArgs[i] = transform(oldTypeArgs[i]);
+        if (newTypeArgs[i] != oldTypeArgs[i]) {
+          argsAllSame = false;
+        }
+      }
+
+      if (argsAllSame && oldBaseType == newBaseType
+          && oldEnclosingType == newEnclosingType) {
+        replacement = type;
+      } else {
+        replacement = typeParameterized.getOracle().getParameterizedType(
+            newBaseType, newEnclosingType, newTypeArgs);
+      }
+    }
+
+    JRawType typeRaw = type.isRawType();
+    if (typeRaw != null) {
+      JGenericType oldBaseType = typeRaw.getBaseType();
+      JGenericType newBaseType = transform(oldBaseType);
+
+      if (oldBaseType == newBaseType) {
+        replacement = type;
+      } else {
+        replacement = newBaseType.getRawType();
+      }
+    }
+
+    JWildcardType typeWild = type.isWildcard();
+    if (typeWild != null) {
+      JClassType oldBound = typeWild.getFirstBound();
+      JClassType newBound = transform(oldBound);
+
+      if (oldBound == newBound) {
+        replacement = type;
+      } else {
+        replacement = typeWild.getOracle().getWildcardType(
+            typeWild.getBoundType(), newBound);
+      }
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/JTypeVisitor.java b/user/src/com/google/gwt/user/rebind/rpc/JTypeVisitor.java
new file mode 100644
index 0000000..8764b6c
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/JTypeVisitor.java
@@ -0,0 +1,174 @@
+/*
+ * 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.rebind.rpc;
+
+import com.google.gwt.core.ext.typeinfo.JAnnotationType;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.core.ext.typeinfo.JWildcardType;
+
+/**
+ * A visitor that traverses the structure of a {@link JType}.
+ */
+class JTypeVisitor {
+  /**
+   * Accept <code>type</code>, traverse its children with
+   * {@link #acceptChildren(JType)}, and then call {@link #endVisit(JType)} on
+   * <code>type</code> itself.
+   */
+  public void accept(JType type) {
+    acceptChildren(type);
+    endVisit(type);
+  }
+
+  /**
+   * Call {@link #accept(JType)} on all children types of <code>type</code>.
+   * The children type of a type are its structural components. For example, an
+   * array type has one child, which is the component type of the array.
+   */
+  protected void acceptChildren(JType type) {
+    JArrayType typeArray = type.isArray();
+    if (typeArray != null) {
+      accept(typeArray.getComponentType());
+      endVisit(typeArray);
+    }
+
+    JParameterizedType typeParameterized = type.isParameterized();
+    if (typeParameterized != null) {
+      accept(typeParameterized.getBaseType());
+      for (JClassType typeArg : typeParameterized.getTypeArgs()) {
+        accept(typeArg);
+      }
+      endVisit(typeParameterized);
+    }
+
+    JRawType typeRaw = type.isRawType();
+    if (typeRaw != null) {
+      accept(typeRaw.getBaseType());
+      endVisit(typeRaw);
+    }
+
+    JWildcardType typeWild = type.isWildcard();
+    if (typeWild != null) {
+      accept(typeWild.getFirstBound());
+      endVisit(typeWild);
+    }
+  }
+
+  protected void endVisit(JAnnotationType typeAnnotation) {
+  }
+
+  protected void endVisit(JArrayType typeArray) {
+  }
+
+  protected void endVisit(JEnumType typeEnum) {
+  }
+
+  protected void endVisit(JGenericType typeGeneric) {
+  }
+
+  protected void endVisit(JParameterizedType typeParameterized) {
+  }
+
+  protected void endVisit(JPrimitiveType typePrimitive) {
+  }
+
+  protected void endVisit(JRawType typeRaw) {
+  }
+
+  protected void endVisit(JRealClassType typeReal) {
+  }
+
+  /**
+   * This endVisit is called on all visited types. By default it dispatches to
+   * one of the more specific endVisit methods, e.g.
+   * {@link #endVisit(JArrayType)}.
+   */
+  protected void endVisit(JType type) {
+    JAnnotationType typeAnnotation = type.isAnnotation();
+    if (typeAnnotation != null) {
+      endVisit(typeAnnotation);
+      return;
+    }
+
+    JArrayType typeArray = type.isArray();
+    if (typeArray != null) {
+      endVisit(typeArray);
+      return;
+    }
+
+    JEnumType typeEnum = type.isEnum();
+    if (typeEnum != null) {
+      endVisit(typeEnum);
+      return;
+    }
+
+    JGenericType typeGeneric = type.isGenericType();
+    if (typeGeneric != null) {
+      endVisit(typeGeneric);
+      return;
+    }
+
+    JParameterizedType typeParameterized = type.isParameterized();
+    if (typeParameterized != null) {
+      endVisit(typeParameterized);
+      return;
+    }
+
+    JPrimitiveType typePrimitive = type.isPrimitive();
+    if (typePrimitive != null) {
+      endVisit(typePrimitive);
+      return;
+    }
+
+    JRawType typeRaw = type.isRawType();
+    if (typeRaw != null) {
+      endVisit(typeRaw);
+      return;
+    }
+
+    JTypeParameter typeParam = type.isTypeParameter();
+    if (typeParam != null) {
+      endVisit(typeParam);
+      return;
+    }
+
+    JWildcardType typeWild = type.isWildcard();
+    if (typeWild != null) {
+      endVisit(typeWild);
+      return;
+    }
+
+    JRealClassType typeReal = (JRealClassType) type;
+    endVisit(typeReal);
+  }
+
+  protected void endVisit(JTypeParameter typeParam) {
+  }
+
+  protected void endVisit(JWildcardType typeWild) {
+  }
+
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index b31aed9..f7c24ee 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -23,7 +23,6 @@
 import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JGenericType;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
-import com.google.gwt.core.ext.typeinfo.JRawType;
 import com.google.gwt.core.ext.typeinfo.JRealClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
@@ -82,38 +81,13 @@
  * </p>
  */
 public class SerializableTypeOracleBuilder {
-  private interface Path {
+
+  interface Path {
     Path getParent();
 
     String toString();
   }
 
-  private enum TypeState {
-    /**
-     * The instantiability of a type has been determined.
-     */
-    CHECK_DONE("Check succeeded"),
-    /**
-     * The instantiability of a type is being checked.
-     */
-    CHECK_IN_PROGRESS("Check in progress"),
-    /**
-     * The instantiability of a type has not been checked.
-     */
-    NOT_CHECKED("Not checked");
-
-    private final String message;
-
-    TypeState(String message) {
-      this.message = message;
-    }
-
-    @Override
-    public String toString() {
-      return message;
-    }
-  }
-
   private class TypeInfoComputed {
 
     /**
@@ -242,6 +216,32 @@
     }
   }
 
+  private enum TypeState {
+    /**
+     * The instantiability of a type has been determined.
+     */
+    CHECK_DONE("Check succeeded"),
+    /**
+     * The instantiability of a type is being checked.
+     */
+    CHECK_IN_PROGRESS("Check in progress"),
+    /**
+     * The instantiability of a type has not been checked.
+     */
+    NOT_CHECKED("Not checked");
+
+    private final String message;
+
+    TypeState(String message) {
+      this.message = message;
+    }
+
+    @Override
+    public String toString() {
+      return message;
+    }
+  }
+
   /**
    * Compares {@link JType}s according to their qualified source names.
    */
@@ -264,8 +264,6 @@
     }
   };
 
-  private static final JClassType[] NO_JCLASSES = new JClassType[0];
-
   static boolean canBeInstantiated(TreeLogger logger, JClassType type,
       TreeLogger.Type logLevel) {
     if (type.isEnum() == null) {
@@ -279,7 +277,8 @@
         // Warn and return false.
         logger.log(
             logLevel,
-            "Was not default instantiable (it must have a zero-argument constructor or no constructors at all)",
+            type.getParameterizedQualifiedSourceName()
+                + " was not default instantiable (it must have a zero-argument constructor or no constructors at all)",
             null);
         return false;
       }
@@ -320,6 +319,16 @@
     return customSerializer;
   }
 
+  static JRealClassType getBaseType(JClassType type) {
+    if (type.isParameterized() != null) {
+      return type.isParameterized().getBaseType();
+    } else if (type.isRawType() != null) {
+      return type.isRawType().getBaseType();
+    }
+
+    return (JRealClassType) type;
+  }
+
   static boolean isAutoSerializable(JClassType type) {
     try {
       JClassType isSerializable = getIsSerializableMarkerInterface(type);
@@ -430,16 +439,6 @@
         return false;
       }
 
-      if (type.isLocalType()) {
-        // Local types cannot be serialized
-        logger.branch(
-            logLevel,
-            type.getParameterizedQualifiedSourceName()
-                + " is a local type; it will be excluded from the set of serializable types",
-            null);
-        return false;
-      }
-
       if (type.isMemberType() && !type.isStatic()) {
         // Non-static member types cannot be serialized
         logger.branch(
@@ -535,6 +534,21 @@
     };
   }
 
+  private static Path createSupertypePath(final Path parent, final JType type,
+      final JClassType subtype) {
+    return new Path() {
+      public Path getParent() {
+        return parent;
+      }
+
+      @Override
+      public String toString() {
+        return "'" + type.getParameterizedQualifiedSourceName()
+            + "' is reachable as a supertype of type '" + subtype + "'";
+      }
+    };
+  }
+
   private static Path createTypeArgumentPath(final Path parent,
       final JClassType type, final int typeArgIndex, final JClassType typeArg) {
     return new Path() {
@@ -625,6 +639,11 @@
     return isAutoSerializable(type) || isManuallySerializable(type);
   }
 
+  private static boolean isDirectlySerializable(JClassType type) {
+    return directlyImplementsMarkerInterface(type)
+        || isManuallySerializable(type);
+  }
+
   private static boolean isManuallySerializable(JClassType type) {
     return findCustomFieldSerializer(type.getOracle(), type) != null;
   }
@@ -646,17 +665,18 @@
   /**
    * Cache of the {@link JClassType} for {@link Collection}.
    */
-  private final JClassType collectionClass;
+  private final JGenericType collectionClass;
 
   private OutputStream logOutputStream;
 
   /**
    * Cache of the {@link JClassType} for {@link Map}.
    */
-  private final JClassType mapClass;
+  private final JGenericType mapClass;
 
   private final Map<JClassType, TreeLogger> rootTypes = new LinkedHashMap<JClassType, TreeLogger>();
 
+  private final TypeConstrainer typeConstrainer;
   private TypeFilter typeFilter = DEFAULT_TYPE_FILTER;
 
   private final TypeOracle typeOracle;
@@ -689,10 +709,11 @@
   public SerializableTypeOracleBuilder(TreeLogger logger, TypeOracle typeOracle)
       throws UnableToCompleteException {
     this.typeOracle = typeOracle;
+    typeConstrainer = new TypeConstrainer(typeOracle);
 
     try {
-      collectionClass = typeOracle.getType(Collection.class.getName());
-      mapClass = typeOracle.getType(Map.class.getName());
+      collectionClass = typeOracle.getType(Collection.class.getName()).isGenericType();
+      mapClass = typeOracle.getType(Map.class.getName()).isGenericType();
     } catch (NotFoundException e) {
       logger.log(TreeLogger.ERROR, null, e);
       throw new UnableToCompleteException();
@@ -741,6 +762,10 @@
       throw new UnableToCompleteException();
     }
 
+    for (TypeInfoComputed tic : typeToTypeInfoComputed.values()) {
+      assert (!tic.isPendingInstantiable());
+    }
+
     pruneUnreachableTypes();
 
     logReachableTypes(logger);
@@ -802,8 +827,9 @@
    * The method is exposed using default access to enable testing.
    */
   final boolean checkTypeInstantiable(TreeLogger logger, JType type,
-      boolean isSpeculative, final Path path) {
-    return checkTypeInstantiable(logger, type, isSpeculative, path, null);
+      boolean isSpeculative, Path path) {
+    return checkTypeInstantiable(logger, type, isSpeculative, path,
+        new HashSet<JClassType>());
   }
 
   /**
@@ -812,7 +838,7 @@
    * except that returns the set of instantiable subtypes.
    */
   boolean checkTypeInstantiable(TreeLogger logger, JType type,
-      boolean isSpeculative, final Path path, Set<JClassType> instSubtypes) {
+      boolean isSpeculative, Path path, Set<JClassType> instSubtypes) {
     assert (type != null);
     if (type.isPrimitive() != null) {
       return true;
@@ -825,15 +851,14 @@
     TreeLogger localLogger = logger.branch(TreeLogger.DEBUG,
         classType.getParameterizedQualifiedSourceName(), null);
 
-    if (!isAllowedByFilter(localLogger, classType, isSpeculative)) {
-      return false;
-    }
-
     JTypeParameter isTypeParameter = classType.isTypeParameter();
     if (isTypeParameter != null) {
       if (typeParametersInRootTypes.contains(isTypeParameter)) {
         return checkTypeInstantiable(localLogger,
-            isTypeParameter.getFirstBound(), isSpeculative, path);
+            isTypeParameter.getFirstBound(), isSpeculative,
+            createTypeArgumentPath(path, isTypeParameter.getDeclaringClass(),
+                isTypeParameter.getOrdinal(), isTypeParameter.getFirstBound()),
+            instSubtypes);
       }
 
       /*
@@ -854,18 +879,9 @@
       return success;
     }
 
-    TypeInfoComputed tic = getTypeInfoComputed(classType, path);
-    if (tic.isPendingInstantiable()) {
-      // just early out and pretend we will succeed
-      return true;
-    } else if (tic.isDone()) {
-      return tic.hasInstantiableSubtypes();
-    }
-    tic.setPendingInstantiable();
-
     JArrayType isArray = classType.isArray();
     if (isArray != null) {
-      return checkArrayInstantiable(localLogger, tic, isSpeculative, path);
+      return checkArrayInstantiable(localLogger, isArray, isSpeculative, path);
     }
 
     if (classType == typeOracle.getJavaLangObject()) {
@@ -877,108 +893,28 @@
           getLogLevel(isSpeculative),
           "In order to produce smaller client-side code, 'Object' is not allowed; consider using a more specific type",
           null);
-      tic.setInstantiable(false);
       return false;
     }
 
     if (classType.isRawType() != null) {
-      TreeLogger rawTypeLogger = localLogger.branch(
+      localLogger.branch(
           TreeLogger.DEBUG,
           "Type '"
               + classType.getQualifiedSourceName()
               + "' should be parameterized to help the compiler produce the smallest code size possible for your module",
           null);
-
-      if (classType.isAssignableTo(collectionClass)
-          || classType.isAssignableTo(mapClass)) {
-        /*
-         * Backwards compatibility. Raw collections or maps force all object
-         * subtypes to be considered. Fall through to the normal class handling.
-         */
-        checkAllSubtypesOfObject(rawTypeLogger, path);
-      }
     }
 
     JClassType originalType = (JClassType) type;
-
-    JRealClassType baseType;
-    if (type.isRawType() != null) {
-      baseType = type.isRawType().getBaseType();
-    } else if (type.isParameterized() != null) {
-      baseType = type.isParameterized().getBaseType();
-    } else {
-      baseType = (JRealClassType) originalType;
-    }
-
-    if (isSpeculative && tic.isDirectlySerializable()) {
+    if (isSpeculative && isDirectlySerializable(originalType)) {
+      // TODO: Our speculative tracking is off.
       isSpeculative = false;
     }
-
-    JClassType[] typeArgs = NO_JCLASSES;
-    JParameterizedType isParameterized = originalType.isParameterized();
-    JGenericType baseAsGenericType = baseType.isGenericType();
-
-    if (isParameterized != null) {
-      typeArgs = isParameterized.getTypeArgs();
-    } else if (baseAsGenericType != null) {
-      List<JClassType> arguments = new ArrayList<JClassType>();
-      for (JTypeParameter typeParameter : baseAsGenericType.getTypeParameters()) {
-        arguments.add(typeParameter.getFirstBound());
-      }
-      typeArgs = arguments.toArray(NO_JCLASSES);
-    }
-
-    boolean parametersOkay = true;
-    JRawType isRaw = originalType.isRawType();
-    if (isParameterized != null || isRaw != null) {
-      assert (baseAsGenericType != null);
-      int numDeclaredParams = baseAsGenericType.getTypeParameters().length;
-      if (numDeclaredParams == typeArgs.length) {
-        for (int i = 0; i < numDeclaredParams; ++i) {
-          JClassType typeArg = typeArgs[i];
-          parametersOkay &= checkTypeArgument(localLogger, baseAsGenericType,
-              i, typeArg, isSpeculative, path);
-        }
-      } else {
-        /*
-         * TODO: Does anyone actually depend on this odious behavior?
-         * 
-         * Backwards compatibility. The number of parameterization arguments
-         * specified via gwt.typeArgs could exceed the number of formal
-         * parameters declared on the generic type. Therefore have to explicitly
-         * visit them here and they must all be instantiable.
-         */
-        for (int i = 0; i < numDeclaredParams; ++i) {
-          JClassType typeArg = typeArgs[i];
-          parametersOkay &= checkTypeInstantiable(localLogger, typeArg,
-              isSpeculative, path);
-        }
-      }
-    }
-
-    // Check whether the type is field serializable
-    // TODO: Should we really continue if parametersOkay is false?
-    boolean isFieldSerializable = parametersOkay
-        && shouldConsiderFieldsForSerialization(localLogger, baseType,
-            isSpeculative)
-        && checkFields(localLogger, baseType, isSpeculative, path);
-
-    // TODO: Figure out logLevel for canBeInstantiated
-    boolean isInstantiable = isFieldSerializable
-        && canBeInstantiated(localLogger, baseType, TreeLogger.WARN);
-
-    boolean anySubtypes = false;
-    if (parametersOkay && baseType.getSubtypes().length > 0) {
-      TreeLogger subtypesLogger = localLogger.branch(TreeLogger.DEBUG,
-          "Analyzing subclasses:", null);
-      anySubtypes = checkSubtypes(subtypesLogger, originalType, baseType,
-          instSubtypes, typeArgs, path, baseType, false, isFieldSerializable);
-    }
-
-    anySubtypes |= isInstantiable;
-
-    tic.setInstantiable(isInstantiable);
-    tic.setInstantiableSubytpes(anySubtypes);
+    //
+    // TreeLogger subtypesLogger = localLogger.branch(TreeLogger.DEBUG,
+    // "Analyzing subclasses:", null);
+    boolean anySubtypes = checkSubtypes(localLogger, originalType,
+        instSubtypes, path);
 
     if (!anySubtypes && !isSpeculative) {
       // No instantiable types were found
@@ -988,7 +924,7 @@
           null);
     }
 
-    return tic.hasInstantiableSubtypes();
+    return anySubtypes;
   }
 
   int getTypeParameterExposure(JGenericType type, int index) {
@@ -1028,36 +964,54 @@
         "Checking all subtypes of Object which qualify for serialization", null);
     JClassType[] allTypes = typeOracle.getJavaLangObject().getSubtypes();
     for (JClassType cls : allTypes) {
-      if (getTypeInfoComputed(cls, parent).isDeclaredSerializable()) {
-        checkTypeInstantiable(localLogger, cls, true, parent);
+      if (isDeclaredSerializable(cls)) {
+        checkTypeInstantiable(localLogger, cls, true, createSubtypePath(parent,
+            cls, typeOracle.getJavaLangObject()));
       }
     }
   }
 
-  private boolean checkArrayInstantiable(TreeLogger localLogger,
-      TypeInfoComputed tic, boolean isSpeculative, final Path path) {
-    JArrayType isArray = tic.getType().isArray();
-    assert (isArray != null);
+  private boolean checkArrayInstantiable(TreeLogger logger, JArrayType array,
+      boolean isSpeculative, Path path) {
 
-    JType leafType = isArray.getLeafType();
+    JType leafType = array.getLeafType();
+    JWildcardType leafWild = leafType.isWildcard();
+    if (leafWild != null) {
+      JArrayType arrayType = getArrayType(typeOracle, array.getRank(),
+          leafWild.getUpperBound());
+      return checkArrayInstantiable(logger, arrayType, isSpeculative, path);
+    }
+
     JClassType leafClass = leafType.isClassOrInterface();
     JTypeParameter isLeafTypeParameter = leafType.isTypeParameter();
     if (isLeafTypeParameter != null
         && !typeParametersInRootTypes.contains(isLeafTypeParameter)) {
       // Don't deal with non root type parameters
-      tic.setInstantiable(false);
-      tic.setInstantiableSubytpes(true);
       return true;
     }
 
-    TreeLogger branch = localLogger.branch(TreeLogger.DEBUG,
+    if (!isAllowedByFilter(logger, array, isSpeculative)) {
+      return false;
+    }
+
+    TypeInfoComputed tic = getTypeInfoComputed(array, path);
+    if (tic.isDone()) {
+      return tic.hasInstantiableSubtypes();
+    } else if (tic.isPendingInstantiable()) {
+      return true;
+    }
+    tic.setPendingInstantiable();
+
+    TreeLogger branch = logger.branch(TreeLogger.DEBUG,
         "Analyzing component type:", null);
     Set<JClassType> instantiableTypes = new HashSet<JClassType>();
 
-    boolean succeeded = checkTypeInstantiable(branch,
-        isArray.getComponentType(), isSpeculative, createArrayComponentPath(
-            isArray, path), instantiableTypes);
+    boolean succeeded = checkTypeInstantiable(branch, array.getComponentType(),
+        isSpeculative, createArrayComponentPath(array, path), instantiableTypes);
     if (succeeded && leafClass != null) {
+      TreeLogger covariantArrayLogger = logger.branch(TreeLogger.DEBUG,
+          "Covariant array types");
+
       /*
        * Compute covariant arrays for arrays of reference types.
        */
@@ -1068,11 +1022,14 @@
           continue;
         }
 
-        for (int rank = 1; rank <= isArray.getRank(); ++rank) {
+        covariantArrayLogger.branch(
+            TreeLogger.DEBUG,
+            getArrayType(typeOracle, array.getRank(), instantiableType).getParameterizedQualifiedSourceName());
+
+        for (int rank = 1; rank <= array.getRank(); ++rank) {
           JArrayType covariantArray = getArrayType(typeOracle, rank,
               instantiableType);
 
-          // TODO: Check the choice of path
           TypeInfoComputed covariantArrayTic = getTypeInfoComputed(
               covariantArray, path);
           covariantArrayTic.setInstantiable(true);
@@ -1101,8 +1058,12 @@
     // All fields on a manual serializable are considered speculative.
     isSpeculative |= typeInfo.isManuallySerializable();
 
+    JClassType baseType = getBaseType(classOrInterface);
+
     boolean allSucceeded = true;
-    JField[] fields = classOrInterface.getFields();
+    // TODO: Propagating the constraints will produce better results as long
+    // as infinite expansion can be avoided in the process.
+    JField[] fields = baseType.getFields();
     if (fields.length > 0) {
       TreeLogger localLogger = logger.branch(TreeLogger.DEBUG,
           "Analyzing the fields of type '"
@@ -1133,160 +1094,135 @@
 
     boolean succeeded = allSucceeded || typeInfo.isManuallySerializable();
     if (succeeded) {
-      getTypeInfoComputed(classOrInterface, parent).setFieldSerializable();
+      typeInfo.setFieldSerializable();
     }
 
     return succeeded;
   }
 
-  private boolean checkFields(TreeLogger logger, JClassType classOrInterface,
-      boolean isSpeculative, Path parent) {
-    TypeInfoComputed typeInfo = getTypeInfoComputed(classOrInterface, parent);
+  private boolean checkSubtype(TreeLogger logger, JClassType classOrInterface,
+      JClassType originalType, boolean isSpeculative, Path parent) {
     if (classOrInterface.isEnum() != null) {
       // The fields of an enum are never serialized; they are always okay.
       return true;
     }
 
+    JParameterizedType isParameterized = classOrInterface.isParameterized();
+    if (isParameterized != null) {
+      if (isRawMapOrRawCollection(classOrInterface)) {
+        /*
+         * Backwards compatibility. Raw collections or maps force all object
+         * subtypes to be considered.
+         */
+        checkAllSubtypesOfObject(logger, parent);
+      } else {
+        TreeLogger paramsLogger = logger.branch(TreeLogger.DEBUG,
+            "Checking parameters of '"
+                + isParameterized.getParameterizedQualifiedSourceName() + "'");
+
+        for (JTypeParameter param : isParameterized.getBaseType().getTypeParameters()) {
+          if (!checkTypeArgument(paramsLogger, isParameterized.getBaseType(),
+              param.getOrdinal(),
+              isParameterized.getTypeArgs()[param.getOrdinal()], true, parent)) {
+            return false;
+          }
+        }
+      }
+    }
+
     // Check all super type fields first (recursively).
     JClassType superType = classOrInterface.getSuperclass();
-    if (superType != null
-        && getTypeInfoComputed(superType, parent).isDeclaredSerializable()) {
-      boolean superTypeOk = checkFields(logger, superType, isSpeculative,
-          parent);
+    if (superType != null && superType.isRawType() != null) {
+      superType = superType.isRawType().asParameterizedByWildcards();
+    }
+
+    if (superType != null && isDeclaredSerializable(superType)) {
+      superType = constrainTypeBy(superType, originalType);
+      if (superType == null) {
+        return false;
+      }
+
+      boolean superTypeOk = false;
+      if (superType != null) {
+        superTypeOk = checkSubtype(logger, superType, originalType,
+            isSpeculative, createSupertypePath(parent, superType,
+                classOrInterface));
+      }
+
       /*
        * If my super type did not check out, then I am not instantiable and we
        * should error out... UNLESS I am *directly* serializable myself, in
        * which case it's ok for me to be the root of a new instantiable
        * hierarchy.
        */
-      if (!superTypeOk && !typeInfo.isDirectlySerializable()) {
+      if (!superTypeOk && !isDirectlySerializable(classOrInterface)) {
         return false;
       }
     }
 
-    return checkDeclaredFields(logger, typeInfo, isSpeculative, parent);
+    TypeInfoComputed tic = getTypeInfoComputed(classOrInterface, parent);
+    return checkDeclaredFields(logger, tic, isSpeculative, parent);
   }
 
   /**
    * Returns <code>true</code> if this type or one of its subtypes is
    * instantiable relative to a known base type.
    */
-  private boolean checkSubtypes(TreeLogger localLogger,
-      JClassType originalType, JRealClassType baseType,
-      Set<JClassType> instSubtypes, JClassType[] typeArgs, final Path path,
-      JClassType currentSubtype, boolean checkSuperFields,
-      boolean superIsFieldSerializable) {
-    assert (currentSubtype instanceof JRealClassType || currentSubtype.isGenericType() != null);
-
-    Path subtypePath = createSubtypePath(path, currentSubtype, originalType);
-    TypeInfoComputed typeInfo = getTypeInfoComputed(currentSubtype, subtypePath);
-
-    TreeLogger subtypeLogger = localLogger.branch(TreeLogger.DEBUG,
-        currentSubtype.getParameterizedQualifiedSourceName(), null);
-
-    /*
-     * If super does not qualify and I am not directly serializable then I can't
-     * be serializable.
-     */
-    boolean fieldSerializable = shouldConsiderFieldsForSerialization(
-        subtypeLogger, currentSubtype, true);
-    if (!superIsFieldSerializable) {
-      fieldSerializable &= typeInfo.isDirectlySerializable();
-    }
-
-    if (fieldSerializable) {
-      if (checkSuperFields) {
-        fieldSerializable &= checkFields(subtypeLogger, typeInfo.getType(),
-            true, subtypePath);
-      } else {
-        fieldSerializable &= checkDeclaredFields(subtypeLogger, typeInfo, true,
-            subtypePath);
-      }
-    }
-
+  private boolean checkSubtypes(TreeLogger logger, JClassType originalType,
+      Set<JClassType> instSubtypes, Path path) {
+    JClassType baseType = getBaseType(originalType);
+    TreeLogger computationLogger = logger.branch(TreeLogger.DEBUG,
+        "Finding possibly instantiable subtypes");
+    List<JClassType> candidates = getPossiblyInstantiableSubtypes(
+        computationLogger, baseType);
     boolean anySubtypes = false;
-    List<JClassType> immediateSubtypes = TypeHierarchyUtils.getImmediateSubtypes(currentSubtype);
-    for (JClassType immediateSubtype : immediateSubtypes) {
-      if (immediateSubtype.isRawType() != null) {
-        immediateSubtype = immediateSubtype.isRawType().getBaseType();
-      }
 
-      checkSuperFields = currentSubtype.isInterface() != null
-          && immediateSubtype.isClass() != null;
-      anySubtypes |= checkSubtypes(localLogger, originalType, baseType,
-          instSubtypes, typeArgs, path, immediateSubtype, checkSuperFields,
-          fieldSerializable);
-    }
-
-    if (!fieldSerializable) {
-      // If the subtype is not instantiable do not check the arguments.
-      return anySubtypes;
-    }
-
-    if (currentSubtype == baseType) {
-      return anySubtypes;
-    }
-
-    JGenericType genericSub = currentSubtype.isGenericType();
-    if (genericSub != null) {
-      TreeLogger paramsLogger = subtypeLogger.branch(TreeLogger.DEBUG,
-          "Checking parameters of '"
-              + genericSub.getParameterizedQualifiedSourceName() + "'");
-      Map<JTypeParameter, Set<JTypeParameter>> subParamsConstrainedBy = subParamsConstrainedBy(
-          baseType, genericSub);
-      for (int i = 0; i < genericSub.getTypeParameters().length; i++) {
-        JTypeParameter param = genericSub.getTypeParameters()[i];
-        TreeLogger paramLogger = paramsLogger.branch(TreeLogger.DEBUG,
-            "Checking param '" + param.getParameterizedQualifiedSourceName()
-                + "'");
-        Set<JTypeParameter> constBy = subParamsConstrainedBy.get(param);
-        if (constBy == null) {
-          fieldSerializable &= checkTypeArgument(paramLogger, genericSub, i,
-              param.getFirstBound(), true, path);
-        } else {
-          boolean paramOK = false;
-          for (JTypeParameter constrained : constBy) {
-            paramOK |= checkTypeArgument(paramLogger, genericSub, i,
-                typeArgs[constrained.getOrdinal()], true, path);
-          }
-          fieldSerializable &= paramOK;
+    TreeLogger verificationLogger = logger.branch(TreeLogger.DEBUG,
+        "Verifying instantiability");
+    for (JClassType candidate : candidates) {
+      if (getBaseType(candidate) == baseType
+          && originalType.isRawType() == null) {
+        // Don't rely on the constrainer when we have perfect information.
+        candidate = originalType;
+      } else {
+        candidate = constrainTypeBy(candidate, originalType);
+        if (candidate == null) {
+          continue;
         }
       }
-    } else {
-      JParameterizedType isParameterized = originalType.isParameterized();
 
-      /*
-       * The subtype is not generic so it must be a concrete parameterization of
-       * the query type. If the query type is also a concrete parameterization
-       * then we should be able to exclude the subtype based on an assignability
-       * check. If the query type does contain type parameters then we assume
-       * that the subtype should be included. In order to be certain, we would
-       * need to perform a full unification between the query type and subtype.
-       */
-      if (isParameterized != null) {
-        HashSet<JTypeParameter> typeParamsInQueryType = new HashSet<JTypeParameter>();
-        recordTypeParametersIn(isParameterized, typeParamsInQueryType);
-
-        if (typeParamsInQueryType.isEmpty()) {
-          if (!isParameterized.isAssignableFrom(currentSubtype)) {
-            subtypeLogger.log(TreeLogger.DEBUG, "Excluding type '"
-                + currentSubtype.getParameterizedQualifiedSourceName()
-                + "' because it is not assignable to '"
-                + isParameterized.getParameterizedQualifiedSourceName() + "'");
-            fieldSerializable = false;
-          }
-        }
-      }
-    }
-
-    if (fieldSerializable
-        && canBeInstantiated(subtypeLogger, currentSubtype, TreeLogger.DEBUG)) {
-      if (instSubtypes != null) {
-        instSubtypes.add(currentSubtype);
+      if (!isAllowedByFilter(verificationLogger, candidate, true)) {
+        continue;
       }
 
-      typeInfo.setInstantiable(true);
-      anySubtypes = true;
+      Path subtypePath = createSubtypePath(path, candidate, originalType);
+      TypeInfoComputed tic = getTypeInfoComputed(candidate, subtypePath);
+      if (tic.isDone()) {
+        anySubtypes |= tic.isInstantiable();
+        continue;
+      } else if (tic.isPendingInstantiable()) {
+        anySubtypes = true;
+        continue;
+      }
+      tic.setPendingInstantiable();
+
+      TreeLogger subtypeLogger = verificationLogger.branch(TreeLogger.DEBUG,
+          candidate.getParameterizedQualifiedSourceName());
+      boolean instantiable = checkSubtype(subtypeLogger, candidate,
+          originalType, true, subtypePath);
+      anySubtypes |= instantiable;
+      tic.setInstantiable(instantiable);
+
+      if (instantiable) {
+        subtypeLogger.branch(TreeLogger.DEBUG, "Is instantiable");
+      }
+
+      // Note we are leaving hasInstantiableSubtypes() as false which might be
+      // wrong but it is only used by arrays and thus it will never be looked at
+      // for this tic.
+      if (instantiable && instSubtypes != null) {
+        instSubtypes.add(candidate);
+      }
     }
 
     return anySubtypes;
@@ -1312,6 +1248,12 @@
    */
   private boolean checkTypeArgument(TreeLogger logger, JGenericType baseType,
       int paramIndex, JClassType typeArg, boolean isSpeculative, Path parent) {
+    JWildcardType isWildcard = typeArg.isWildcard();
+    if (isWildcard != null) {
+      return checkTypeArgument(logger, baseType, paramIndex,
+          isWildcard.getUpperBound(), isSpeculative, parent);
+    }
+
     JArrayType typeArgAsArray = typeArg.isArray();
     if (typeArgAsArray != null) {
       JTypeParameter parameterOfTypeArgArray = typeArgAsArray.getLeafType().isTypeParameter();
@@ -1323,7 +1265,7 @@
           TypeParameterFlowInfo otherFlowInfo = getFlowInfo(baseType,
               paramIndex);
           if (otherFlowInfo.getExposure() >= 0
-              && flowInfoForArrayParam.infiniteArrayExpansionPathBetween(otherFlowInfo)) {
+              && otherFlowInfo.isTransitivelyAffectedBy(flowInfoForArrayParam)) {
             logger.branch(
                 getLogLevel(isSpeculative),
                 "Cannot serialize type '"
@@ -1375,11 +1317,59 @@
     }
   }
 
+  /**
+   * If <code>type</code>'s base class does not inherit from
+   * <code>superType</code>'s base class, then return <code>type</code>
+   * unchanged. If it does, then return a subtype of <code>type</code> that
+   * includes all values in both <code>type</code> and <code>superType</code>.
+   * If there are definitely no such values, return <code>null</code>.
+   */
+  private JClassType constrainTypeBy(JClassType type, JClassType superType) {
+    return typeConstrainer.constrainTypeBy(type, superType);
+  }
+
   private TypeParameterFlowInfo getFlowInfo(JGenericType type, int index) {
     return typeParameterExposureComputer.computeTypeParameterExposure(type,
         index);
   }
 
+  /**
+   * Returns the subtypes of a given base type as parameterized by wildcards.
+   */
+  private List<JClassType> getPossiblyInstantiableSubtypes(TreeLogger logger,
+      JClassType baseType) {
+    assert (baseType == getBaseType(baseType));
+
+    List<JClassType> possiblyInstantiableTypes = new ArrayList<JClassType>();
+    if (baseType == typeOracle.getJavaLangObject()) {
+      return possiblyInstantiableTypes;
+    }
+
+    List<JClassType> candidates = new ArrayList<JClassType>();
+    candidates.add(baseType);
+    candidates.addAll(Arrays.asList(baseType.getSubtypes()));
+
+    for (JClassType subtype : candidates) {
+      JClassType subtypeBase = getBaseType(subtype);
+      if (maybeInstantiable(logger, subtypeBase)) {
+        /*
+         * Convert the generic type into a parameterization that only includes
+         * wildcards.
+         */
+        JGenericType isGeneric = subtype.isGenericType();
+        if (isGeneric != null) {
+          subtype = isGeneric.asParameterizedByWildcards();
+        } else {
+          assert (subtype instanceof JRealClassType);
+        }
+
+        possiblyInstantiableTypes.add(subtype);
+      }
+    }
+
+    return possiblyInstantiableTypes;
+  }
+
   private TypeInfoComputed getTypeInfoComputed(JClassType type, Path path) {
     TypeInfoComputed tic = typeToTypeInfoComputed.get(type);
     if (tic == null) {
@@ -1394,6 +1384,22 @@
     return isAllowedByFilter(logger, typeFilter, classType, isSpeculative);
   }
 
+  /**
+   * Returns <code>true</code> if the type is Collection<? extends Object> or
+   * Map<? extends Object, ? extends Object>.
+   */
+  private boolean isRawMapOrRawCollection(JClassType type) {
+    if (type.asParameterizationOf(collectionClass) == collectionClass.asParameterizedByWildcards()) {
+      return true;
+    }
+
+    if (type.asParameterizationOf(mapClass) == mapClass.asParameterizedByWildcards()) {
+      return true;
+    }
+
+    return false;
+  }
+
   private void logPath(TreeLogger logger, Path path) {
     if (path == null) {
       return;
@@ -1449,6 +1455,16 @@
     }
   }
 
+  private boolean maybeInstantiable(TreeLogger logger, JClassType type) {
+    boolean success = canBeInstantiated(logger, type, TreeLogger.DEBUG)
+        && shouldConsiderFieldsForSerialization(logger, type, true);
+    if (success) {
+      logger.branch(TreeLogger.DEBUG,
+          type.getParameterizedQualifiedSourceName() + " might be instantiable");
+    }
+    return success;
+  }
+
   private boolean mightNotBeExposed(JGenericType baseType, int paramIndex) {
     TypeParameterFlowInfo flowInfo = getFlowInfo(baseType, paramIndex);
     return flowInfo.getMightNotBeExposed() || isManuallySerializable(baseType);
@@ -1498,36 +1514,4 @@
       typeToTypeInfoComputed.remove(type);
     }
   }
-
-  /**
-   * Returns a map from each parameter in the subtype to the set of parameters,
-   * if any, in the supertype which constrain that parameter.
-   */
-  private Map<JTypeParameter, Set<JTypeParameter>> subParamsConstrainedBy(
-      JClassType superclass, JGenericType subclass) {
-
-    Map<JTypeParameter, Set<JTypeParameter>> newTypeParameters = new LinkedHashMap<JTypeParameter, Set<JTypeParameter>>();
-
-    JGenericType isGenericSuper = superclass.isGenericType();
-    if (isGenericSuper != null) {
-      JParameterizedType parameterization = subclass.asParameterizationOf(isGenericSuper);
-      JClassType[] paramArgs = parameterization.getTypeArgs();
-      for (int i = 0; i < paramArgs.length; ++i) {
-        Set<JTypeParameter> typeParamsInParamArg = new HashSet<JTypeParameter>();
-        recordTypeParametersIn(paramArgs[i], typeParamsInParamArg);
-
-        for (JTypeParameter arg : typeParamsInParamArg) {
-          Set<JTypeParameter> constBy = newTypeParameters.get(arg);
-          if (constBy == null) {
-            constBy = new LinkedHashSet<JTypeParameter>();
-            newTypeParameters.put(arg, constBy);
-          }
-
-          constBy.add(isGenericSuper.getTypeParameters()[i]);
-        }
-      }
-    }
-
-    return newTypeParameters;
-  }
 }
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeConstrainer.java b/user/src/com/google/gwt/user/rebind/rpc/TypeConstrainer.java
new file mode 100644
index 0000000..c089795
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeConstrainer.java
@@ -0,0 +1,362 @@
+/*
+ * 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.rebind.rpc;
+
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.core.ext.typeinfo.JWildcardType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class defines the method
+ * {@link #constrainTypeBy(JClassType, JClassType)}.
+ */
+public class TypeConstrainer {
+  /**
+   * Check whether two base types have any subclasses in common. Note: this
+   * could surely be implemented much more efficiently.
+   */
+  private static boolean baseTypesOverlap(JClassType type1, JClassType type2) {
+    assert (type1 == getBaseType(type1));
+    assert (type2 == getBaseType(type2));
+
+    if (type1 == type2) {
+      return true;
+    }
+
+    HashSet<JClassType> subtypes1 = new HashSet<JClassType>();
+    subtypes1.add(type1);
+    for (JClassType sub1 : type1.getSubtypes()) {
+      subtypes1.add(getBaseType(sub1));
+    }
+
+    List<JClassType> subtypes2 = new ArrayList<JClassType>();
+    subtypes2.add(type2);
+    for (JClassType sub2 : type2.getSubtypes()) {
+      subtypes2.add(getBaseType(sub2));
+    }
+
+    for (JClassType sub2 : subtypes2) {
+      if (subtypes1.contains(sub2)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  private static JClassType getBaseType(JClassType type) {
+    return SerializableTypeOracleBuilder.getBaseType(type);
+  }
+
+  private static boolean isRealOrParameterized(JClassType type) {
+    if (type.isParameterized() != null) {
+      return true;
+    }
+    if (type instanceof JRealClassType) {
+      return true;
+    }
+    return false;
+  }
+
+  private static JClassType[] makeArray(JClassType... classTypes) {
+    return classTypes;
+  }
+
+  /**
+   * Check whether <code>param</code> occurs anywhere within <code>type</code>.
+   */
+  private static boolean occurs(final JTypeParameter param, JClassType type) {
+    class OccursVisitor extends JTypeVisitor {
+      boolean foundIt = false;
+
+      @Override
+      public void endVisit(JTypeParameter seenParam) {
+        if (seenParam == param) {
+          foundIt = true;
+        }
+      }
+    }
+
+    OccursVisitor visitor = new OccursVisitor();
+    visitor.accept(type);
+    return visitor.foundIt;
+  }
+
+  private int freshTypeVariableCounter;
+
+  private final TypeOracle typeOracle;
+
+  public TypeConstrainer(TypeOracle typeOracle) {
+    this.typeOracle = typeOracle;
+  }
+
+  /**
+   * Return a subtype of <code>subType</code> that includes all values in both
+   * <code>subType</code> and <code>superType</code>. The returned type
+   * must have the same base type as <code>subType</code>. If there are
+   * definitely no such values, return <code>null</code>.
+   */
+  public JClassType constrainTypeBy(JClassType subType, JClassType superType) {
+    JParameterizedType superAsParameterized = superType.isParameterized();
+    if (superAsParameterized == null) {
+      // If the supertype is not parameterized, it will not be possible to
+      // constrain
+      // the subtype further.
+      return subType;
+    }
+
+    // Replace each wildcard in the subType with a fresh type variable.
+    // These type variables will be the ones that are constrained.
+    Map<JTypeParameter, JClassType> constraints = new HashMap<JTypeParameter, JClassType>();
+    JClassType subWithWildcardsReplaced = replaceWildcardsWithFreshTypeVariables(
+        subType, constraints);
+
+    // Rewrite subType so that it has the same base type as superType.
+    JParameterizedType subAsParameterized = subWithWildcardsReplaced.asParameterizationOf(superAsParameterized.getBaseType());
+    if (subAsParameterized == null) {
+      // The subtype's base does not inherit from the supertype's base,
+      // so again no constraint will be possible.
+      return subType;
+    }
+
+    // Check the rewritten type against superType
+    if (!typesMatch(subAsParameterized, superAsParameterized, constraints)) {
+      // The types are completely incompatible
+      return null;
+    }
+
+    // Apply the revised constraints to the original type
+    return substitute(subWithWildcardsReplaced, constraints);
+  }
+
+  /**
+   * Check whether two types can have any values in common. The
+   * <code>constraints</code> field holds known constraints for type
+   * parameters that appear in <code>type1</code>; this method may take
+   * advantage of those constraints in its decision, and it may tighten them so
+   * long as the tightening does not reject any values from the overlap of the
+   * two types.
+   * 
+   * As an invariant, no key in <code>constraints</code> may occur inside any
+   * value in <code>constraints</code>.
+   * 
+   * Note that this algorithm looks for overlap matches in the arguments of
+   * parameterized types rather than looking for exact matches. Looking for
+   * overlaps simplifies the algorithm but returns true more often than it has
+   * to.
+   */
+  boolean typesMatch(JClassType type1, JClassType type2,
+      Map<JTypeParameter, JClassType> constraints) {
+    JGenericType type1Generic = type1.isGenericType();
+    if (type1Generic != null) {
+      return typesMatch(type1Generic.asParameterizedByWildcards(), type2,
+          constraints);
+    }
+
+    JGenericType type2Generic = type2.isGenericType();
+    if (type2Generic != null) {
+      return typesMatch(type1, type2Generic.asParameterizedByWildcards(),
+          constraints);
+    }
+
+    JWildcardType type1Wild = type1.isWildcard();
+    if (type1Wild != null) {
+      return typesMatch(type1Wild.getUpperBound(), type2, constraints);
+    }
+
+    JWildcardType type2Wild = type2.isWildcard();
+    if (type2Wild != null) {
+      return typesMatch(type1, type2Wild.getUpperBound(), constraints);
+    }
+
+    JRawType type1Raw = type1.isRawType();
+    if (type1Raw != null) {
+      return typesMatch(type1Raw.asParameterizedByWildcards(), type2,
+          constraints);
+    }
+
+    JRawType type2Raw = type2.isRawType();
+    if (type2Raw != null) {
+      return typesMatch(type1, type2Raw.asParameterizedByWildcards(),
+          constraints);
+    }
+
+    assert (type1Generic == null);
+    assert (type2Generic == null);
+    assert (type1Wild == null);
+    assert (type2Wild == null);
+    assert (type1Raw == null);
+    assert (type2Raw == null);
+
+    if (type1 == type2) {
+      return true;
+    }
+
+    if (constraints.containsKey(type1)) {
+      JTypeParameter type1Parameter = (JTypeParameter) type1;
+      JClassType type2Class = type2;
+      JClassType type1Bound = constraints.get(type1Parameter);
+      assert (!occurs(type1Parameter, type1Bound));
+      if (!typesMatch(type1Bound, type2, constraints)) {
+        return false;
+      }
+
+      if (type1Bound.isAssignableFrom(type2Class)) {
+        constraints.put(type1Parameter, type2Class);
+      }
+    }
+
+    if (type1 == typeOracle.getJavaLangObject()) {
+      return true;
+    }
+
+    if (type2 == typeOracle.getJavaLangObject()) {
+      return true;
+    }
+
+    JTypeParameter type1Param = type1.isTypeParameter();
+    if (type1Param != null) {
+      // It would be nice to check that type1Param's bound is a match
+      // for type2, but that can introduce infinite recursions.
+      return true;
+    }
+
+    JTypeParameter type2Param = type2.isTypeParameter();
+    if (type2Param != null) {
+      // It would be nice to check that type1Param's bound is a match
+      // for type2, but that can introduce infinite recursions.
+      return true;
+    }
+
+    JArrayType type1Array = type1.isArray();
+    JArrayType type2Array = type2.isArray();
+    if (type1Array != null && type2Array != null) {
+      if (typesMatch(type1Array.getComponentType(),
+          type2Array.getComponentType(), constraints)) {
+        return true;
+      }
+    }
+
+    if (isRealOrParameterized(type1) && isRealOrParameterized(type2)) {
+      JClassType baseType1 = getBaseType(type1);
+      JClassType baseType2 = getBaseType(type2);
+      JParameterizedType type1Parameterized = type1.isParameterized();
+      JParameterizedType type2Parameterized = type2.isParameterized();
+
+      if (baseType1 == baseType2 && type1Parameterized != null
+          && type2Parameterized != null) {
+        // type1 and type2 are parameterized types with the same base type;
+        // compare their arguments
+        JClassType[] args1 = type1Parameterized.getTypeArgs();
+        JClassType[] args2 = type2Parameterized.getTypeArgs();
+        boolean allMatch = true;
+        for (int i = 0; i < args1.length; i++) {
+          if (!typesMatch(args1[i], args2[i], constraints)) {
+            allMatch = false;
+          }
+        }
+
+        if (allMatch) {
+          return true;
+        }
+      } else {
+        // The types have different base types, so just compare the base types
+        // for overlap.
+        if (baseTypesOverlap(baseType1, baseType2)) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * The same as {@link #typesMatch(JClassType, JClassType, Map)}, but
+   * additionally support primitive types as well as class types.
+   */
+  boolean typesMatch(JType type1, JType type2,
+      Map<JTypeParameter, JClassType> constraints) {
+    if (type1 == type2) {
+      // This covers the case where both are primitives
+      return true;
+    }
+
+    JClassType type1Class = type1.isClassOrInterface();
+    JClassType type2Class = type2.isClassOrInterface();
+    if (type1Class != null && type2Class != null
+        && typesMatch(type1Class, type2Class, constraints)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Replace all wildcards in <code>type</code> with a fresh type variable.
+   * For each type variable created, add an entry in <code>constraints</code>
+   * mapping the type variable to its upper bound.
+   */
+  private JClassType replaceWildcardsWithFreshTypeVariables(JClassType type,
+      final Map<JTypeParameter, JClassType> constraints) {
+
+    JModTypeVisitor replacer = new JModTypeVisitor() {
+      @Override
+      public void endVisit(JWildcardType wildcardType) {
+        JTypeParameter newParam = new JTypeParameter("TP$"
+            + freshTypeVariableCounter++, -1);
+        newParam.setBounds(makeArray(typeOracle.getJavaLangObject()));
+        constraints.put(newParam, wildcardType.getUpperBound());
+        replacement = newParam;
+      }
+    };
+
+    return replacer.transform(type);
+  }
+
+  /**
+   * Substitute all occurrences in <code>type</code> of type parameters in
+   * <code>constraints</code> for a wildcard bounded by the parameter's entry
+   * in <code>constraints</code>. If the argument is <code>null</code>,
+   * return <code>null</code>.
+   */
+  private JClassType substitute(JClassType type,
+      final Map<JTypeParameter, JClassType> constraints) {
+    JModTypeVisitor substituter = new JModTypeVisitor() {
+      @Override
+      public void endVisit(JTypeParameter param) {
+        JClassType constr = constraints.get(param);
+        if (constr != null) {
+          replacement = constr;
+        }
+      }
+    };
+    return substituter.transform(type);
+  }
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeParameterExposureComputer.java b/user/src/com/google/gwt/user/rebind/rpc/TypeParameterExposureComputer.java
index b3ddb78..e3a2ba4 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeParameterExposureComputer.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeParameterExposureComputer.java
@@ -192,6 +192,35 @@
       return checkDirectExposure();
     }
 
+    boolean isTransitivelyAffectedBy(TypeParameterFlowInfo flowInfo) {
+      Boolean result = isTransitivelyAffectedByCache.get(flowInfo);
+      if (result != null) {
+        return result;
+      }
+
+      HashSet<TypeParameterFlowInfo> affectedBy = new HashSet<TypeParameterFlowInfo>();
+      Set<TypeParameterFlowInfo> affectedByWorklist = new LinkedHashSet<TypeParameterFlowInfo>();
+      affectedByWorklist.add(this);
+
+      result = false;
+      while (!affectedByWorklist.isEmpty()) {
+        TypeParameterFlowInfo currFlowInfo = affectedByWorklist.iterator().next();
+        affectedByWorklist.remove(currFlowInfo);
+
+        if (currFlowInfo == flowInfo) {
+          result = true;
+          break;
+        }
+
+        if (affectedBy.add(currFlowInfo)) {
+          affectedByWorklist.addAll(currFlowInfo.getAffectedBy());
+        }
+      }
+
+      isTransitivelyAffectedByCache.put(flowInfo, result);
+      return result;
+    }
+
     boolean markExposedAsArray(int dim) {
       if (exposure >= dim) {
         return false;
@@ -291,35 +320,6 @@
       return flowInfo;
     }
 
-    private boolean isTransitivelyAffectedBy(TypeParameterFlowInfo flowInfo) {
-      Boolean result = isTransitivelyAffectedByCache.get(flowInfo);
-      if (result != null) {
-        return result;
-      }
-
-      HashSet<TypeParameterFlowInfo> affectedBy = new HashSet<TypeParameterFlowInfo>();
-      Set<TypeParameterFlowInfo> affectedByWorklist = new LinkedHashSet<TypeParameterFlowInfo>();
-      affectedByWorklist.add(this);
-
-      result = false;
-      while (!affectedByWorklist.isEmpty()) {
-        TypeParameterFlowInfo currFlowInfo = affectedByWorklist.iterator().next();
-        affectedByWorklist.remove(currFlowInfo);
-
-        if (currFlowInfo == flowInfo) {
-          result = true;
-          break;
-        }
-
-        if (affectedBy.add(currFlowInfo)) {
-          affectedByWorklist.addAll(currFlowInfo.getAffectedBy());
-        }
-      }
-
-      isTransitivelyAffectedByCache.put(flowInfo, result);
-      return result;
-    }
-
     private void recordCausesExposure(JGenericType type, int index, int level) {
       assert (index < type.getTypeParameters().length);
       TypeParameterFlowInfo flowInfo = getFlowInfo(type, index);
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 7b6b05e..00a16b0 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -428,7 +428,9 @@
   }
 
   public long readLong() {
-    return Long.parseLong(extract(), 16);
+    // Keep synchronized with LongLib. The wire format are the two component
+    // parts of the double in the client code.
+    return (long) readDouble() + (long) readDouble();
   }
 
   public short readShort() {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 6946d47..474dd3b 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -544,10 +544,15 @@
 
   public void writeLong(long fieldValue) {
     /*
-     * Marshal down to client as a string; if we tried to marshal as a number
-     * the JSON eval would lose precision.
+     * Client code represents longs internally as an array of two Numbers. In
+     * order to make serialization of longs faster, we'll send the component
+     * parts so that the value can be directly reconstituted on the client.
      */
-    writeString(Long.toString(fieldValue, 16));
+    double[] parts = makeLongComponents((int) (fieldValue >> 32),
+        (int) fieldValue);
+    assert parts.length == 2;
+    writeDouble(parts[0]);
+    writeDouble(parts[1]);
   }
 
   @Override
diff --git a/user/test/com/google/gwt/core/CoreSuite.java b/user/test/com/google/gwt/core/CoreSuite.java
index 8084e34..0bc8039 100644
--- a/user/test/com/google/gwt/core/CoreSuite.java
+++ b/user/test/com/google/gwt/core/CoreSuite.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.client.GWTTest;
 import com.google.gwt.core.client.JavaScriptExceptionTest;
+import com.google.gwt.core.client.JsArrayTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
 import junit.framework.Test;
@@ -30,6 +31,7 @@
 
     // $JUnit-BEGIN$
     suite.addTestSuite(JavaScriptExceptionTest.class);
+    suite.addTestSuite(JsArrayTest.class);
     suite.addTestSuite(GWTTest.class);
     // $JUnit-END$
 
diff --git a/user/test/com/google/gwt/core/client/JsArrayTest.java b/user/test/com/google/gwt/core/client/JsArrayTest.java
new file mode 100644
index 0000000..a06bf8e
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/JsArrayTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.core.client;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests JsArray variants.
+ */
+public class JsArrayTest extends GWTTestCase {
+
+  private static class JsPoint extends JavaScriptObject {
+    protected JsPoint() {
+    }
+
+    public final native int x() /*-{
+      return this.x;
+    }-*/;
+
+    public final native int y() /*-{
+      return this.y;
+    }-*/;
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.Core";
+  }
+
+  public void testJsArray() {
+    // All the test arrays start with 3 elements.
+    JsArray<JsPoint> jsArray = makeJsArray();
+    assertEquals(3, jsArray.length());
+
+    // Get the three points and make sure they are what we think.
+    JsPoint p0 = jsArray.get(0);
+    JsPoint p1 = jsArray.get(1);
+    JsPoint p2 = jsArray.get(2);
+
+    assertEquals(0, p0.x());
+    assertEquals(1, p0.y());
+    assertEquals(2, p1.x());
+    assertEquals(3, p1.y());
+    assertEquals(4, p2.x());
+    assertEquals(5, p2.y());
+
+    // Make sure the '3' element is null.
+    assertNull(jsArray.get(3));
+
+    // Make a new point and stick it in the '3' slot. It should come back with
+    // reference equality intact, and the array length should be bumped to 4.
+    JsPoint p3 = makeJsPoint(6, 7);
+    jsArray.set(3, p3);
+    assertEquals(p3, jsArray.get(3));
+    assertEquals(4, jsArray.length());
+
+    // Stick a non-JSO value in the '4' slot. Getting it should cause a type
+    // error in hosted mode.
+    if (!GWT.isScript()) {
+      try {
+        jsArray.<JsArrayString> cast().set(4, "bubba");
+        jsArray.get(4);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  public void testJsArrayBoolean() {
+    // All the test arrays start with 3 elements.
+    JsArrayBoolean jsArray = makeJsArrayBoolean();
+    assertEquals(3, jsArray.length());
+
+    // Get the three points and make sure they are what we think.
+    assertEquals(true, jsArray.get(0));
+    assertEquals(false, jsArray.get(1));
+    assertEquals(true, jsArray.get(2));
+
+    // Make sure getting the '3' element throws an exception in hosted mode
+    // (this won't happen in web mode).
+    if (!GWT.isScript()) {
+      try {
+        jsArray.get(3);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+
+    // Stick a new boolean in the '3' slot. It should come back intact, and the
+    // array length should be bumped to 4.
+    jsArray.set(3, false);
+    assertEquals(false, jsArray.get(3));
+    assertEquals(4, jsArray.length());
+
+    // Stick a non-boolean value in the '4' slot. Getting it should cause a type
+    // error in hosted mode.
+    if (!GWT.isScript()) {
+      try {
+        jsArray.<JsArrayString> cast().set(4, "bubba");
+        jsArray.get(4);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  public void testJsArrayInteger() {
+    // All the test arrays start with 3 elements.
+    JsArrayInteger jsArray = makeJsArrayInteger();
+    assertEquals(3, jsArray.length());
+
+    // Get the three points and make sure they are what we think.
+    assertEquals(0, jsArray.get(0));
+    assertEquals(1, jsArray.get(1));
+    assertEquals(2, jsArray.get(2));
+
+    // Make sure getting the '3' element throws an exception in hosted mode
+    // (this won't happen in web mode).
+    if (!GWT.isScript()) {
+      try {
+        jsArray.get(3);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+
+    // Stick a new number in the '3' slot. It should come back intact, and the
+    // array length should be bumped to 4.
+    jsArray.set(3, 3);
+    assertEquals(3, jsArray.get(3));
+    assertEquals(4, jsArray.length());
+
+    // Stick a non-numeric value in the '4' slot. Getting it should cause a type
+    // error in hosted mode.
+    if (!GWT.isScript()) {
+      try {
+        jsArray.<JsArrayString> cast().set(4, "bubba");
+        jsArray.get(4);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  public void testJsArrayNumber() {
+    // All the test arrays start with 3 elements.
+    JsArrayNumber jsArray = makeJsArrayNumber();
+    assertEquals(3, jsArray.length());
+
+    // Get the three points and make sure they are what we think.
+    assertEquals(0.0, jsArray.get(0));
+    assertEquals(1.1, jsArray.get(1));
+    assertEquals(2.2, jsArray.get(2));
+
+    // Make sure getting the '3' element throws an exception in hosted mode
+    // (this won't happen in web mode).
+    if (!GWT.isScript()) {
+      try {
+        jsArray.get(3);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+
+    // Stick a new number in the '3' slot. It should come back intact, and the
+    // array length should be bumped to 4.
+    jsArray.set(3, 3.0);
+    assertEquals(3.0, jsArray.get(3));
+    assertEquals(4, jsArray.length());
+
+    // Stick a non-numeric value in the '4' slot. Getting it should cause a type
+    // error in hosted mode.
+    if (!GWT.isScript()) {
+      try {
+        jsArray.<JsArrayString> cast().set(4, "bubba");
+        jsArray.get(4);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  public void testJsArrayString() {
+    // All the test arrays start with 3 elements.
+    JsArrayString jsArray = makeJsArrayString();
+    assertEquals(3, jsArray.length());
+
+    // Get the three points and make sure they are what we think.
+    String s0 = jsArray.get(0);
+    String s1 = jsArray.get(1);
+    String s2 = jsArray.get(2);
+
+    assertEquals("foo", s0);
+    assertEquals("bar", s1);
+    assertEquals("baz", s2);
+
+    // Make sure the '3' element is null.
+    assertNull(jsArray.get(3));
+
+    // Stick a new string in the '3' slot. It should come back intact, and the
+    // array length should be bumped to 4.
+    jsArray.set(3, "tintin");
+    assertEquals("tintin", jsArray.get(3));
+    assertEquals(4, jsArray.length());
+
+    // Stick a non-String value in the '4' slot. Getting it should cause a type
+    // error in hosted mode.
+    if (!GWT.isScript()) {
+      try {
+        jsArray.<JsArrayBoolean> cast().set(4, true);
+        jsArray.get(4);
+        fail("Expected an exception getting an invalid value in hosted mode");
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  private native JsArray<JsPoint> makeJsArray() /*-{
+    return [
+      { x: 0, y: 1},
+      { x: 2, y: 3},
+      { x: 4, y: 5},
+    ];
+  }-*/;
+
+  private native JsArrayBoolean makeJsArrayBoolean() /*-{
+    return [true, false, true];
+  }-*/;
+
+  private native JsArrayInteger makeJsArrayInteger() /*-{
+    return [0, 1, 2];
+  }-*/;
+
+  private native JsArrayNumber makeJsArrayNumber() /*-{
+    return [0.0, 1.1, 2.2];
+  }-*/;
+
+  private native JsArrayString makeJsArrayString() /*-{
+    return ['foo', 'bar', 'baz'];
+  }-*/;
+
+  private native JsPoint makeJsPoint(int newx, int newy) /*-{
+    return {x: newx, y: newy};
+  }-*/;
+}
diff --git a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
index 7edd64d..a01dc69 100644
--- a/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/NativeLongTest.java
@@ -28,6 +28,9 @@
    * constant fold them. The problem is that if you write assertEquals(2L,
    * 4L/2L), the compiler will emit assertEquals(2L, 2L).
    */
+  private static volatile byte BYTE_FOUR = (byte) 4;
+  private static volatile char CHAR_FOUR = (char) 4;
+  private static volatile int INT_FOUR = 4;
   private static volatile long LONG_100 = 100L;
   private static volatile long LONG_1234 = 0x1234123412341234L;
   private static volatile long LONG_1234_DECIMAL = 1234123412341234L;
@@ -37,11 +40,13 @@
   private static volatile long LONG_DEADBEEF = 0xdeadbeefdeadbeefL;
   private static volatile long LONG_DEADBEEF12341234 = 0xdeadbeef12341234L;
   private static volatile long LONG_FFFFFFFF = 0xFFFFFFFFL;
+  private static volatile long LONG_FOUR = 4L;
   private static volatile long LONG_ONE = 1L;
   private static volatile long LONG_THREE = 3L;
   private static volatile long LONG_TWO = 2L;
   private static volatile long LONG_TWO_PWR_32 = 0x100000000L;
   private static volatile long LONG_ZERO = 0L;
+  private static volatile short SHORT_FOUR = (short) 4;
 
   @Override
   public String getModuleName() {
@@ -129,10 +134,18 @@
 
   public void testModifyingOps() {
     long l = 20;
-    l += 10;
-    assertEquals(31, ++l);
-    assertEquals(31, l++);
-    assertEquals(32, l);
+    l += INT_FOUR;
+    assertEquals(25, ++l);
+    assertEquals(25, l++);
+    assertEquals(26, l);
+    l += BYTE_FOUR;
+    assertEquals(30, l);
+    l += CHAR_FOUR;
+    assertEquals(34, l);
+    l += SHORT_FOUR;
+    assertEquals(38, l);
+    l += INT_FOUR;
+    assertEquals(42, l);
   }
 
   public void testShift() {
@@ -140,6 +153,11 @@
     assertEquals(LONG_ONE << 12, (LONG_ONE << 60) >>> (48));
 
     assertTrue((LONG_ONE << 35) > (LONG_ONE << 30));
+    assertEquals(0x10L, LONG_ONE << BYTE_FOUR);
+    assertEquals(0x10L, LONG_ONE << CHAR_FOUR);
+    assertEquals(0x10L, LONG_ONE << SHORT_FOUR);
+    assertEquals(0x10L, LONG_ONE << INT_FOUR);
+    assertEquals(0x10L, LONG_ONE << LONG_FOUR);
 
     assertEquals(1L, LONG_TWO_PWR_32 >> 32);
     assertEquals(1L, LONG_TWO_PWR_32 >>> 32);
diff --git a/user/test/com/google/gwt/user/ClassInitTest.java b/user/test/com/google/gwt/user/ClassInitTest.java
new file mode 100644
index 0000000..8939c5f
--- /dev/null
+++ b/user/test/com/google/gwt/user/ClassInitTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import com.google.gwt.junit.GWTMockUtilities;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+
+/**
+ * Tests that every class in com.google.gwt.user.client.ui can be init'd by the
+ * real Java runtime. By ensuring this, we ensure that these classes all may be
+ * referenced mocked out by pure Java unit tests, e.g. with EasyMock Class
+ * Extension
+ */
+public class ClassInitTest extends TestCase {
+  private static final String DOT_CLASS = ".class";
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    GWTMockUtilities.disarm();
+  }
+
+  @Override
+  public void tearDown() {
+    GWTMockUtilities.restore();
+  }
+
+  public void testOne() throws ClassNotFoundException {
+    String packageName = "com.google.gwt.user.client.ui";
+    String path = packageNameToPath(packageName);
+    File directory = pathToResourceDirectory(path);
+
+    if (directory.exists()) {
+      String[] files = directory.list();
+      for (String file : files) {
+        if (file.endsWith(DOT_CLASS)) {
+          String classname = classFileToClassName(file);
+          Class.forName(packageName + "." + classname);
+        }
+      }
+    }
+  }
+
+  private String classFileToClassName(String file) {
+    return file.substring(0, file.length() - DOT_CLASS.length());
+  }
+
+  private File pathToResourceDirectory(String name) {
+    File directory = new File(getClass().getResource(name).getFile());
+    return directory;
+  }
+
+  private String packageNameToPath(String name) {
+    name = "/" + name.replace('.', '/');
+    return name;
+  }
+}
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index c63ee5f..b34b49c 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -20,6 +20,7 @@
 import com.google.gwt.user.client.CookieTest;
 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.CaptionPanelTest;
 import com.google.gwt.user.client.ui.CheckBoxTest;
 import com.google.gwt.user.client.ui.CompositeTest;
@@ -35,6 +36,7 @@
 import com.google.gwt.user.client.ui.DialogBoxTest;
 import com.google.gwt.user.client.ui.DisclosurePanelTest;
 import com.google.gwt.user.client.ui.DockPanelTest;
+import com.google.gwt.user.client.ui.ElementWrappingTest;
 import com.google.gwt.user.client.ui.FastStringMapTest;
 import com.google.gwt.user.client.ui.FlexTableTest;
 import com.google.gwt.user.client.ui.FlowPanelTest;
@@ -55,6 +57,8 @@
 import com.google.gwt.user.client.ui.RadioButtonTest;
 import com.google.gwt.user.client.ui.RichTextAreaTest;
 import com.google.gwt.user.client.ui.ScrollPanelTest;
+import com.google.gwt.user.client.ui.SimpleCheckBoxTest;
+import com.google.gwt.user.client.ui.SimpleRadioButtonTest;
 import com.google.gwt.user.client.ui.SplitPanelTest;
 import com.google.gwt.user.client.ui.StackPanelTest;
 import com.google.gwt.user.client.ui.TabBarTest;
@@ -81,6 +85,7 @@
         "Test for suite for the com.google.gwt.ui module");
 
     suite.addTestSuite(AbsolutePanelTest.class);
+    suite.addTestSuite(AnchorTest.class);
     suite.addTestSuite(CaptionPanelTest.class);
     suite.addTestSuite(CheckBoxTest.class);
     suite.addTestSuite(ClippedImagePrototypeTest.class);
@@ -99,6 +104,7 @@
     suite.addTestSuite(DisclosurePanelTest.class);
     suite.addTestSuite(DockPanelTest.class);
     suite.addTestSuite(DOMTest.class);
+    suite.addTestSuite(ElementWrappingTest.class);
     suite.addTestSuite(FastStringMapTest.class);
     suite.addTestSuite(FlexTableTest.class);
     suite.addTestSuite(FlowPanelTest.class);
@@ -122,6 +128,8 @@
     suite.addTestSuite(RadioButtonTest.class);
     suite.addTestSuite(RichTextAreaTest.class);
     suite.addTestSuite(ScrollPanelTest.class);
+    suite.addTestSuite(SimpleCheckBoxTest.class);
+    suite.addTestSuite(SimpleRadioButtonTest.class);
     suite.addTestSuite(SplitPanelTest.class);
     suite.addTestSuite(StackPanelTest.class);
     suite.addTestSuite(TabBarTest.class);
@@ -135,6 +143,7 @@
     suite.addTestSuite(WidgetOnLoadTest.class);
     suite.addTestSuite(WindowTest.class);
     suite.addTestSuite(XMLTest.class);
+    suite.addTestSuite(ClassInitTest.class);
 
     return suite;
   }
diff --git a/user/test/com/google/gwt/user/client/ui/AnchorTest.java b/user/test/com/google/gwt/user/client/ui/AnchorTest.java
new file mode 100644
index 0000000..b981b54
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/AnchorTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.HasDirection;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
+
+/**
+ * Tests for {@link Anchor}.
+ */
+public class AnchorTest extends GWTTestCase {
+
+  private static final String TEST_URL0 = "http://www.google.com/";
+  private static final String TEST_URL1 = "http://code.google.com/";
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTest";
+  }
+
+  public void testProperties() {
+    Anchor anchor = new Anchor("foo", TEST_URL0);
+    assertEquals("foo", anchor.getText());
+    assertEquals("foo", anchor.getHTML());
+    assertEquals(TEST_URL0, anchor.getHref());
+
+    anchor.setText("bar");
+    assertEquals("bar", anchor.getText());
+
+    anchor.setHTML("baz");
+    assertEquals("baz", anchor.getHTML());
+
+    anchor.setHref(TEST_URL1);
+    assertEquals(TEST_URL1, anchor.getHref());
+
+    anchor.setDirection(HasDirection.Direction.RTL);
+    assertEquals(HasDirection.Direction.RTL, anchor.getDirection());
+
+    anchor.setWordWrap(true);
+    assertEquals(true, anchor.getWordWrap());
+
+    anchor.setTabIndex(42);
+    assertEquals(42, anchor.getTabIndex());
+  }
+
+  private final class TestClickListener implements ClickListener {
+    private int clicks = 0;
+    private Widget lastSender;
+
+    public void onClick(Widget sender) {
+      clicks++;
+      lastSender = sender;
+    }
+
+    public int getClicks() {
+      return clicks;
+    }
+
+    public Widget getLastSender() {
+      return lastSender;
+    }
+  }
+
+  public void testNoAttributes() {
+    Anchor anchor = new Anchor();
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals(0, DOM.getChildCount(anchor.getElement()));
+
+    final String[] attrs = new String[] {
+        "href", "name", "id", "rel", "ref", "target"};
+    for (String attribute : attrs) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testScriptAnchor() {
+    Anchor anchor = new Anchor("Foo");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("Foo", anchor.getText());
+    assertAttributeHasValue("javascript:", anchor.getElement(), "href");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref", "target"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testScriptAnchorWithHTML() {
+    Anchor anchor = new Anchor("<span>Foo</span>", true);
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("SPAN", DOM.getChild(anchor.getElement(), 0).getTagName());
+    assertAttributeHasValue("javascript:", anchor.getElement(), "href");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref", "target"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testEvents() {
+    Anchor anchor = new Anchor("Trigger obscure JavaScript things");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    TestClickListener testListener = new TestClickListener();
+    anchor.addClickListener(testListener);
+
+    assertEquals(0, testListener.getClicks());
+    triggerEvent(anchor.getElement(), "click", false, "MouseEvents");
+    assertEquals(1, testListener.getClicks());
+    assertEquals(anchor, testListener.getLastSender());
+  }
+
+  public void testLink() {
+    Anchor anchor = new Anchor("Click me!", "http://nowhere.org/");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("Click me!", anchor.getText());
+    assertAttributeHasValue("http://nowhere.org/", anchor.getElement(), "href");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref", "target"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testLinkWithHTML() {
+    Anchor anchor = new Anchor("<span>Foo</span>", true,
+        "http://still.nowhere.org/");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("SPAN", DOM.getChild(anchor.getElement(), 0).getTagName());
+    assertTrue("<span>Foo</span>".equalsIgnoreCase(anchor.getHTML()));
+
+    assertAttributeHasValue("http://still.nowhere.org/", anchor.getElement(),
+        "href");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref", "target"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testLinkWithTarget() {
+    Anchor anchor = new Anchor("Click me!",
+        "http://and.now.a.word.from.our.sponsor.org/", "popup");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("Click me!", anchor.getText());
+    assertAttributeHasValue("http://and.now.a.word.from.our.sponsor.org/",
+        anchor.getElement(), "href");
+    assertAttributeHasValue("popup", anchor.getElement(), "target");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  public void testLinkWithHTMLAndTarget() {
+    Anchor anchor = new Anchor("<span>Foo</span>", true,
+        "http://more.ads.com/", "_blank");
+
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    assertEquals(1, DOM.getChildCount(p.getElement()));
+    assertEquals("A", DOM.getChild(p.getElement(), 0).getTagName());
+    assertEquals("SPAN", DOM.getChild(anchor.getElement(), 0).getTagName());
+    assertTrue("<span>Foo</span>".equalsIgnoreCase(anchor.getHTML()));
+
+    assertAttributeHasValue("http://more.ads.com/", anchor.getElement(), "href");
+    assertAttributeHasValue("_blank", anchor.getElement(), "target");
+
+    for (String attribute : new String[] {
+        "name", "id", "rel", "ref"}) {
+      assertAttributeNotPresent(attribute, anchor.getElement());
+    }
+  }
+
+  /**
+   * Tests that the getters interact with the generic setter correctly.
+   */
+  public void testGetterRoundtrip() {
+    Anchor anchor = new Anchor();
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    anchor.getElement().setAttribute("href",
+        "http://yet.another.made.up.url.org/");
+    assertEquals("http://yet.another.made.up.url.org/", anchor.getHref());
+
+    anchor.getElement().setAttribute("target", "_blank");
+    assertEquals("_blank", anchor.getTarget());
+
+    anchor.getElement().setAttribute("name", "Marty");
+    assertEquals("Marty", anchor.getName());
+
+    anchor.getElement().setAttribute("tabIndex", "23");
+    assertEquals(23, anchor.getTabIndex());
+  }
+
+  /**
+   * Tests that the setters interact with the generic getter correctly.
+   */
+  public void testSetterRoundtrip() {
+    Anchor anchor = new Anchor();
+    Panel p = getTestPanel();
+    p.add(anchor);
+
+    anchor.setHref("http://duh.no.more.ideas.net/");
+    assertEquals("http://duh.no.more.ideas.net/",
+        anchor.getElement().getAttribute("href"));
+
+    anchor.setTarget("_top");
+    assertEquals("_top", anchor.getElement().getAttribute("target"));
+
+    anchor.setName("Hieronymous");
+    assertEquals("Hieronymous", anchor.getElement().getAttribute("name"));
+
+    anchor.setName("Hieronymous");
+    assertEquals("Hieronymous", anchor.getElement().getAttribute("name"));
+
+    anchor.setTabIndex(42);
+    assertEquals(42, Integer.parseInt(anchor.getElement().getAttribute("tabIndex")));
+  }
+
+  /**
+   * Constructs a simple panel for testing.
+   * 
+   * @return Panel, attached to the root panel.
+   */
+  private Panel getTestPanel() {
+    Panel p = new FlowPanel();
+    RootPanel.get().add(p);
+    return p;
+  }
+
+  /**
+   * Asserts that a given attribute is not present on an element.
+   * 
+   * @param element The element to check.
+   * @param attribute The attribute to check.
+   */
+  private static void assertAttributeNotPresent(String attribute,
+      Element element) {
+    String value = element.getPropertyString(attribute);
+    assertTrue(attribute + " not present", (value == null)
+        || (value.equals("")));
+  }
+
+  /**
+   * Asserts that a given attribute has the expected valued.
+   * 
+   * @param expected Expected value for the attribute.
+   * @param element Element to check on.
+   * @param attribute Attribute to check.
+   */
+  private static void assertAttributeHasValue(String expected, Element element,
+      String attribute) {
+    assertEquals("Attribute " + attribute + " has value '" + expected + "'",
+        expected, element.getPropertyString(attribute));
+  }
+
+  /**
+   * Triggers events in a cross-browser way.
+   * 
+   * TODO: Refactor this and other utility methods into a Utility class or
+   * common base.
+   * 
+   * @param element the element on which to trigger the event
+   * @param eventType the type of event to trigger
+   * @param canBubble true if the event can bubble
+   * @param eventClass the class of event
+   */
+  public native void triggerEvent(Element element, String eventType,
+      boolean canBubble, String eventClass) /*-{
+    // TODO: This is convenient for now, but could be a lot simpler if we added
+    // a GWTtier API for event triggering.
+    canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+    if (element.fireEvent) {
+      var evt = element.ownerDocument.createEventObject();
+      element.fireEvent('on' + eventType, evt);
+    } else {
+      var evt = document.createEvent(eventClass);
+      evt.initEvent(eventType, canBubble, true);
+      element.dispatchEvent(evt);
+    }
+  }-*/;
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DOMTest.java b/user/test/com/google/gwt/user/client/ui/DOMTest.java
index c58977f..9177c28 100644
--- a/user/test/com/google/gwt/user/client/ui/DOMTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DOMTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -46,6 +46,7 @@
     return (elem.childNodes.length);
   }-*/;
 
+  @Override
   public String getModuleName() {
     return "com.google.gwt.user.UserTest";
   }
@@ -130,6 +131,38 @@
   }
 
   /**
+   * Tests {@link DOM#getAbsoluteTop(Element)} and
+   * {@link DOM#getAbsoluteLeft(Element)} for consistency when the parent
+   * element has a border.
+   * 
+   */
+  public void testGetAbsolutePositionWithPixelBorders() {
+    final Element outer = DOM.createDiv();
+    final Element inner = DOM.createDiv();
+
+    outer.getStyle().setProperty("position", "relative");
+    outer.getStyle().setProperty("width", "200px");
+    outer.getStyle().setProperty("height", "200px");
+
+    inner.getStyle().setProperty("position", "absolute");
+    inner.getStyle().setProperty("top", "30px");
+    inner.getStyle().setProperty("left", "40px");
+    inner.setInnerText("inner");
+
+    outer.appendChild(inner);
+    RootPanel.getBodyElement().appendChild(outer);
+
+    // Get the position without a border
+    int absTop = inner.getAbsoluteTop();
+    int absLeft = inner.getAbsoluteLeft();
+
+    // Get the position with a border
+    outer.getStyle().setProperty("border", "2px solid blue");
+    assertEquals(2, inner.getAbsoluteTop() - absTop);
+    assertEquals(2, inner.getAbsoluteLeft() - absLeft);
+  }
+
+  /**
    * Tests the ability to do a parent-ward walk in the DOM.
    */
   public void testGetParent() {
@@ -167,18 +200,18 @@
   public void testIsOrHasChild() {
     Element div = DOM.createDiv();
     Element childDiv = DOM.createDiv();
-    
+
     assertFalse(DOM.isOrHasChild(div, childDiv));
     assertTrue(DOM.isOrHasChild(div, div));
-    
+
     DOM.appendChild(div, childDiv);
     assertTrue(DOM.isOrHasChild(div, childDiv));
     assertFalse(DOM.isOrHasChild(childDiv, div));
-    
+
     DOM.appendChild(RootPanel.getBodyElement(), div);
     assertTrue(DOM.isOrHasChild(div, childDiv));
     assertTrue(DOM.isOrHasChild(div, div));
-    assertFalse(DOM.isOrHasChild(childDiv, div));    
+    assertFalse(DOM.isOrHasChild(childDiv, div));
   }
 
   /**
@@ -234,6 +267,7 @@
     assertEndsWith("b0.gif", DOM.getImgSrc(image));
     delayTestFinish(2000);
     new Timer() {
+      @Override
       public void run() {
         assertEndsWith("b0.gif", DOM.getElementProperty(image, "src"));
         finishTest();
@@ -273,6 +307,7 @@
     assertEndsWith("a1.gif", DOM.getImgSrc(images[2]));
     delayTestFinish(2000);
     new Timer() {
+      @Override
       public void run() {
         assertEndsWith("a1.gif", DOM.getElementProperty(images[0], "src"));
         assertEndsWith("b1.gif", DOM.getElementProperty(images[1], "src"));
@@ -314,6 +349,7 @@
     assertEndsWith("a2.gif", DOM.getImgSrc(images[2]));
     delayTestFinish(2000);
     new Timer() {
+      @Override
       public void run() {
         assertEndsWith("b2.gif", DOM.getElementProperty(images[0], "src"));
         assertEndsWith("a2.gif", DOM.getElementProperty(images[1], "src"));
@@ -358,6 +394,7 @@
     assertEndsWith("b3.gif", DOM.getImgSrc(images[3]));
     delayTestFinish(2000);
     new Timer() {
+      @Override
       public void run() {
         assertEndsWith("b3.gif", DOM.getElementProperty(images[0], "src"));
         assertEndsWith("a3.gif", DOM.getElementProperty(images[1], "src"));
@@ -407,6 +444,7 @@
     assertEndsWith("b4.gif", DOM.getImgSrc(images[4]));
     delayTestFinish(2000);
     new Timer() {
+      @Override
       public void run() {
         assertEndsWith("a4.gif", DOM.getElementProperty(images[0], "src"));
         assertEndsWith("a4.gif", DOM.getElementProperty(images[1], "src"));
diff --git a/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
new file mode 100644
index 0000000..7c927ef
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests for various methods of the form {@link Button#wrap(Element)}.
+ */
+public class ElementWrappingTest extends GWTTestCase {
+
+  private static final String TEST_URL = "http://www.google.com/";
+  private static final String IMG_URL = "http://www.google.com/images/logo_sm.gif";
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTest";
+  }
+
+  public void testAnchor() {
+    ensureDiv().setInnerHTML("<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
+    Anchor anchor = Anchor.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(anchor);
+    assertEquals(TEST_URL, anchor.getHref());
+    assertEquals("myAnchor", anchor.getText());
+  }
+
+  public void testButton() {
+    ensureDiv().setInnerHTML("<button id='foo'>myButton</button>");
+    Button button = Button.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(button);
+    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 testTextBox() {
+    ensureDiv().setInnerHTML("<input type='text' id='foo'></input>");
+    TextBox textBox = TextBox.wrap(Document.get().getElementById("foo"));
+
+    assertExistsAndAttached(textBox);
+  }
+
+  private void assertExistsAndAttached(Widget widget) {
+    assertNotNull(widget);
+    assertTrue(widget.isAttached());
+  }
+
+  private Element ensureDiv() {
+    Document doc = Document.get();
+    Element div = doc.getElementById("wrapperDiv");
+    if (div == null) {
+      div = doc.createDivElement();
+      div.setId("wrapperDiv");
+      doc.getBody().appendChild(div);
+    }
+    return div;
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/SimpleCheckBoxTest.java b/user/test/com/google/gwt/user/client/ui/SimpleCheckBoxTest.java
new file mode 100644
index 0000000..7df2ec6
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/SimpleCheckBoxTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests for {@link SimpleCheckBox}.
+ */
+public class SimpleCheckBoxTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTest";
+  }
+
+  public void testProperties() {
+    SimpleCheckBox checkbox = new SimpleCheckBox();
+
+    checkbox.setName("myName");
+    assertEquals("myName", checkbox.getName());
+
+    checkbox.setTabIndex(42);
+    assertEquals(42, checkbox.getTabIndex());
+
+    checkbox.setEnabled(false);
+    assertEquals(false, checkbox.isEnabled());
+
+    // Test the 'checked' state across attachment and detachment
+    // (this value has a tendency to get lost on some browsers).
+    checkbox.setChecked(true);
+    assertEquals(true, checkbox.isChecked());
+
+    RootPanel.get().add(checkbox);
+    assertEquals(true, checkbox.isChecked());
+
+    RootPanel.get().remove(checkbox);
+    assertEquals(true, checkbox.isChecked());
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/SimpleRadioButtonTest.java b/user/test/com/google/gwt/user/client/ui/SimpleRadioButtonTest.java
new file mode 100644
index 0000000..9910868
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/SimpleRadioButtonTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.ui;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests for {@link SimpleRadioButton}.
+ */
+public class SimpleRadioButtonTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.UserTest";
+  }
+
+  public void testProperties() {
+    SimpleRadioButton radio = new SimpleRadioButton("myName");
+    assertEquals("myName", radio.getName());
+
+    radio.setTabIndex(42);
+    assertEquals(42, radio.getTabIndex());
+
+    radio.setEnabled(false);
+    assertEquals(false, radio.isEnabled());
+
+    // Test the 'checked' state across attachment and detachment
+    // (this value has a tendency to get lost on some browsers).
+    radio.setChecked(true);
+    assertEquals(true, radio.isChecked());
+
+    RootPanel.get().add(radio);
+    assertEquals(true, radio.isChecked());
+
+    RootPanel.get().remove(radio);
+    assertEquals(true, radio.isChecked());
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/TabBarTest.java b/user/test/com/google/gwt/user/client/ui/TabBarTest.java
index a84c5e9..9b99077 100644
--- a/user/test/com/google/gwt/user/client/ui/TabBarTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabBarTest.java
@@ -20,7 +20,7 @@
 import com.google.gwt.user.client.Element;
 
 /**
- * TODO: document me.
+ * Tests the {@link TabBar} widget.
  */
 public class TabBarTest extends GWTTestCase {
   int selected;
@@ -108,6 +108,32 @@
     assertEquals("baz", bar.getTabHTML(1));
   }
 
+  public void testSetTextAndHTML() {
+    final TabBar bar = createTabBar();
+    bar.addTab("foo");
+    bar.addTab("bar");
+    bar.addTab("baz");
+    bar.addTab(new Grid(0, 0));
+
+    bar.setTabText(1, "w00t");
+    assertEquals("w00t", bar.getTabHTML(1));
+
+    // toLowerCase() is necessary in these assertions because IE capitalizes
+    // HTML tags read from innerHTML.
+    bar.setTabHTML(1, "<i>w00t!</i>");
+    assertEquals("<i>w00t!</i>", bar.getTabHTML(1).toLowerCase());
+
+    // Set the text knowing that we currently have an HTML.  This should replace
+    // the HTML with a Label.
+    bar.setTabText(1, "<b>w00t</b>");
+    assertEquals("<b>w00t</b>", bar.getTabHTML(1).toLowerCase());
+
+    // Set the text knowing that we currently have a Grid.  This should replace
+    // the Grid with a Label.
+    bar.setTabText(3, "w00t");
+    assertEquals("w00t", bar.getTabHTML(3));
+  }
+
   /**
    * Verify that if a tab contains a widget, {@link TabBar#getTabHTML(int)}
    * returns the HTML of the focusable element with the widget inside of it.
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index 8050a43..5915a67 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -21,11 +21,13 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JGenericType;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JRawType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefLoader;
 import com.google.gwt.dev.javac.CompilationUnit;
@@ -46,8 +48,10 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -84,6 +88,11 @@
     }
 
     @Override
+    public int hashCode() {
+      return sourceName.hashCode() * (maybeInstantiated ? 17 : 43);
+    }
+
+    @Override
     public String toString() {
       return "{ " + sourceName + ", " + Boolean.toString(maybeInstantiated)
           + " }";
@@ -856,6 +865,85 @@
   }
 
   /**
+   * Test the situation where an abstract class has an unconstrained type
+   * parameter but all of its concrete subclasses add helpful constraints to it.
+   * 
+   * @throws NotFoundException
+   * @throws UnableToCompleteException
+   */
+  public void testConcreteClassesConstrainATypeParameter()
+      throws NotFoundException, UnableToCompleteException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public abstract class Holder<T extends Serializable> implements Serializable {\n");
+      code.append("  T x;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Holder", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class StringHolder extends Holder<String> implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("StringHolder", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class DateHolder extends Holder<Date> implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("DateHolder", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class Date implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Date", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class UnrelatedClass implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("UnrelatedClass", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JGenericType holder = to.getType("Holder").isGenericType();
+    JClassType stringHolder = to.getType("StringHolder");
+    JClassType dateHolder = to.getType("DateHolder");
+    JClassType unrelatedClass = to.getType("UnrelatedClass");
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, holder.getRawType());
+    SerializableTypeOracle so = sob.build(logger);
+
+    JClassType string = to.getType(String.class.getCanonicalName());
+    JClassType date = to.getType("Date");
+
+    assertSerializableTypes(so, holder.getRawType(), stringHolder, dateHolder,
+        string, date);
+    assertFieldSerializable(so, holder.getRawType());
+    assertInstantiable(so, stringHolder);
+    assertInstantiable(so, dateHolder);
+    assertInstantiable(so, string);
+    assertInstantiable(so, date);
+    assertNotInstantiableOrFieldSerializable(so, unrelatedClass);
+  }
+
+  /**
    * Tests that a method signature which returns an Array type also includes the
    * possible covariant array types which could contain a serializable type.
    */
@@ -884,6 +972,162 @@
   }
 
   /**
+   * If the query type extends a raw type, be sure to pick up the parameters of
+   * the raw supertype.
+   * 
+   * @throws UnableToCompleteException
+   * @throws NotFoundException
+   */
+  public void testExtensionFromRaw1() throws UnableToCompleteException,
+      NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class HashSet<T extends SerClass> implements Serializable {\n");
+      code.append("  T[] x;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("HashSet", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class NameSet extends HashSet implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("NameSet", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class SerClass implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("SerClass", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class SerClassSub extends SerClass {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("SerClassSub", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JGenericType hashSet = to.getType("HashSet").isGenericType();
+    JClassType nameSet = to.getType("NameSet");
+    JClassType serClass = to.getType("SerClass");
+    JClassType serClassSub = to.getType("SerClassSub");
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, nameSet);
+    SerializableTypeOracle so = sob.build(logger);
+
+    JArrayType arrayOfSerClass = to.getArrayType(serClass);
+    JArrayType arrayOfSerClassSub = to.getArrayType(serClassSub);
+
+    assertSerializableTypes(so, hashSet.getRawType(), nameSet, serClass,
+        serClassSub, arrayOfSerClass, arrayOfSerClassSub);
+    assertFieldSerializable(so, hashSet.getRawType());
+    assertNotInstantiable(so, hashSet.getRawType());
+    assertInstantiable(so, nameSet);
+    assertInstantiable(so, serClass);
+    assertInstantiable(so, serClassSub);
+    assertInstantiable(so, arrayOfSerClass);
+    assertInstantiable(so, arrayOfSerClassSub);
+  }
+
+  /**
+   * If a subtype of a root type extends from the raw version of that root type,
+   * then when visiting the fields of the raw version, take advantage of
+   * information from the original root type.
+   * 
+   * @throws UnableToCompleteException
+   * @throws NotFoundException
+   */
+  public void testExtensionFromRaw2() throws UnableToCompleteException,
+      NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class HashSet<T extends Serializable> implements Serializable {\n");
+      code.append("  T[] x;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("HashSet", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class NameSet<T extends SerClass> extends HashSet implements Serializable {\n");
+      code.append("  T exposed;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("NameSet", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class SerClass implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("SerClass", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class SerClassSub extends SerClass {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("SerClassSub", code));
+    }
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class UnrelatedClass implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("UnrelatedClass", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JClassType string = to.getType(String.class.getCanonicalName());
+    JGenericType hashSet = to.getType("HashSet").isGenericType();
+    JGenericType nameSet = to.getType("NameSet").isGenericType();
+    JClassType unrelatedClass = to.getType("UnrelatedClass");
+    JClassType serClass = to.getType("SerClass");
+    JClassType serClassSub = to.getType("SerClassSub");
+    JParameterizedType hashSetOfString = to.getParameterizedType(hashSet,
+        makeArray(string));
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, hashSetOfString);
+    SerializableTypeOracle so = sob.build(logger);
+
+    JArrayType arrayOfString = to.getArrayType(string);
+
+    assertSerializableTypes(so, hashSet.getRawType(), nameSet.getRawType(),
+        string, arrayOfString, serClass, serClassSub);
+    assertInstantiable(so, hashSet.getRawType());
+    assertInstantiable(so, nameSet.getRawType());
+    assertInstantiable(so, string);
+    assertInstantiable(so, serClass);
+    assertInstantiable(so, serClassSub);
+    assertNotInstantiable(so, unrelatedClass);
+  }
+
+  /**
    * Expansion via parameterized types, where the type is exposed.
    */
   public void testInfiniteParameterizedTypeExpansionCase1()
@@ -1134,6 +1378,131 @@
   }
 
   /**
+   * Tests that the type constrainer can accurately detect when an interface
+   * matches another type.
+   */
+  public void testNonOverlappingInterfaces() throws UnableToCompleteException,
+      NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public interface Intf1 extends Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf1", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public interface Intf2 extends Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf2", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public interface Intf3 extends Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf3", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class Implements12 implements Intf1, Intf2 {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Implements12", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ImplementsNeither implements Serializable {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ImplementsNeither", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public interface List<T> extends Serializable  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("List", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ListOfIntf1 implements List<Intf1>  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ListOfIntf1", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ListOfIntf2 implements List<Intf2>  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ListOfIntf2", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ListOfIntf3 implements List<Intf3>  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ListOfIntf3", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ListOfImplements12 implements List<Implements12>  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ListOfImplements12", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class ListOfImplementsNeither implements List<ImplementsNeither>  {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ListOfImplementsNeither", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JClassType intf1 = to.getType("Intf1");
+    JClassType intf2 = to.getType("Intf2");
+    JClassType intf3 = to.getType("Intf3");
+    JClassType implements12 = to.getType("Implements12");
+    JClassType implementsNeither = to.getType("ImplementsNeither");
+    JGenericType list = to.getType("List").isGenericType();
+    JClassType listOfIntf1 = to.getType("ListOfIntf1");
+    JClassType listOfIntf2 = to.getType("ListOfIntf2");
+    JClassType listOfIntf3 = to.getType("ListOfIntf3");
+    JClassType listOfImplements12 = to.getType("ListOfImplements12");
+    JClassType listOfImplementsNeither = to.getType("ListOfImplementsNeither");
+
+    TypeConstrainer typeConstrainer = new TypeConstrainer(to);
+    Map<JTypeParameter, JClassType> emptyConstraints = new HashMap<JTypeParameter, JClassType>();
+    assertTrue(typeConstrainer.typesMatch(intf1, intf2, emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(intf1, intf3, emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(implements12, intf1, emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(implements12, intf3,
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(implementsNeither, intf1,
+        emptyConstraints));
+
+    JParameterizedType parameterizedListOfIntf1 = to.getParameterizedType(list,
+        makeArray(intf1));
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, parameterizedListOfIntf1);
+    SerializableTypeOracle so = sob.build(logger);
+
+    assertSerializableTypes(so, listOfIntf1, listOfIntf2, listOfImplements12);
+    assertNotFieldSerializable(so, listOfIntf3);
+    assertNotFieldSerializable(so, listOfImplementsNeither);
+  }
+
+  /**
    * Tests that a method signature which has no serializable types will result
    * in a failure.
    */
@@ -1239,6 +1608,63 @@
   }
 
   /**
+   * Tests that adding a raw collection as a root type pulls in the world.
+   * 
+   * @throws UnableToCompleteException
+   * @throws NotFoundException
+   */
+  public void testRawCollection() throws UnableToCompleteException,
+      NotFoundException {
+
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("import java.util.Collection;\n");
+      code.append("public interface List<T> extends Serializable, Collection<T> {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("List", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("import java.util.Collection;\n");
+      code.append("public class LinkedList<T> implements List<T> {\n");
+      code.append("  T head;");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("LinkedList", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("import java.util.Collection;\n");
+      code.append("public class RandomClass implements Serializable {\n");
+      // TODO(mmendez): Lex, this should fail, but your fix will allow it if
+      // we get here from a raw collection.
+      // code.append(" Object obj;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("RandomClass", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JGenericType list = to.getType("List").isGenericType();
+    JGenericType linkedList = to.getType("LinkedList").isGenericType();
+    JClassType randomClass = to.getType("RandomClass");
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, list.getRawType());
+    SerializableTypeOracle so = sob.build(logger);
+
+    assertInstantiable(so, linkedList.getRawType());
+    assertInstantiable(so, randomClass);
+  }
+
+  /**
    * Tests that raw type with type parameters that are instantiable are
    * themselves instantiable.
    * 
@@ -1284,6 +1710,42 @@
     assertSerializableTypes(so, rawA, serializableClass);
   }
 
+  /**
+   * Tests that a type paramter that occurs within its bounds will not result in
+   * infinite recursion.
+   * 
+   * @throws UnableToCompleteException
+   * @throws NotFoundException
+   */
+  public void testRootTypeParameterWithSelfInBounds()
+      throws UnableToCompleteException, NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("import java.io.Serializable;\n");
+      code.append("public class A<Ta extends A<Ta>> implements Serializable {\n");
+      code.append("  Ta ta;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("A", code));
+    }
+
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JGenericType a = to.getType("A").isGenericType();
+    JClassType rawA = a.getRawType();
+    JTypeParameter ta = a.getTypeParameters()[0];
+
+    SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+        logger, to);
+    sob.addRootType(logger, ta);
+    SerializableTypeOracle so = sob.build(logger);
+
+    assertSerializableTypes(so, rawA);
+  }
+
   /*
    * Tests the isAssignable test for deciding whether a subclass should be
    * pulled in.
@@ -1521,6 +1983,131 @@
   }
 
   /**
+   * Miscellaneous direct tests of {@link TypeConstrainer}.
+   * 
+   * @throws UnableToCompleteException
+   * @throws NotFoundException
+   */
+  public void testTypeConstrainer() throws UnableToCompleteException,
+      NotFoundException {
+    Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+    addStandardClasses(units);
+
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public interface Intf1 {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf1", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public interface Intf2 {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf2", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public interface Intf3 {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Intf3", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public class Implements12 implements Intf1, Intf2 {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Implements12", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public class ImplementsNeither {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("ImplementsNeither", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public class Sup {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Sup", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public class Sub extends Sup {\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Sub", code));
+    }
+    {
+      StringBuilder code = new StringBuilder();
+      code.append("public class Holder<T> {\n");
+      code.append("  T x;\n");
+      code.append("}\n");
+      units.add(createMockCompilationUnit("Holder", code));
+    }
+    TreeLogger logger = createLogger();
+    TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+    JClassType intf1 = to.getType("Intf1");
+    JClassType intf2 = to.getType("Intf2");
+    JClassType intf3 = to.getType("Intf3");
+    JClassType implements12 = to.getType("Implements12");
+    JClassType implementsNeither = to.getType("ImplementsNeither");
+    JClassType string = to.getType(String.class.getCanonicalName());
+    JClassType sub = to.getType("Sub");
+    JClassType sup = to.getType("Sup");
+    JGenericType holder = to.getType("Holder").isGenericType();
+
+    JClassType arrayOfInt = to.getArrayType(JPrimitiveType.INT);
+    JClassType arrayOfFloat = to.getArrayType(JPrimitiveType.FLOAT);
+    JClassType arrayOfString = to.getArrayType(string);
+    JClassType arrayOfWildExtString = to.getArrayType(to.getWildcardType(
+        BoundType.EXTENDS, string));
+    JClassType holderOfString = to.getParameterizedType(holder,
+        makeArray(to.getWildcardType(BoundType.EXTENDS, string)));
+    JClassType holderOfSub = to.getParameterizedType(holder,
+        makeArray(to.getWildcardType(BoundType.EXTENDS, sub)));
+    JClassType holderOfSup = to.getParameterizedType(holder,
+        makeArray(to.getWildcardType(BoundType.EXTENDS, sup)));
+
+    TypeConstrainer typeConstrainer = new TypeConstrainer(to);
+    Map<JTypeParameter, JClassType> emptyConstraints = new HashMap<JTypeParameter, JClassType>();
+
+    assertTrue(typeConstrainer.typesMatch(intf1, intf2, emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(intf1, intf3, emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(implements12, intf1, emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(implements12, intf3,
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(implementsNeither, intf1,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(to.getJavaLangObject(),
+        arrayOfString, emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(arrayOfString,
+        to.getJavaLangObject(), emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(sub, sup, emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(sub, sub, emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(arrayOfFloat, arrayOfFloat,
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(arrayOfFloat, arrayOfInt,
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(arrayOfFloat, arrayOfString,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(arrayOfString, arrayOfString,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(arrayOfString, arrayOfWildExtString,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(holderOfSub, holderOfSup,
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(holderOfSub, holderOfString,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(holder.getRawType(), holderOfSub,
+        emptyConstraints));
+    assertTrue(typeConstrainer.typesMatch(holderOfSub, holder.getRawType(),
+        emptyConstraints));
+    assertFalse(typeConstrainer.typesMatch(holder.getRawType(), intf1,
+        emptyConstraints));
+
+    assertTrue(emptyConstraints.isEmpty());
+  }
+
+  /**
    * Tests root types that have type parameters.
    * 
    * @throws UnableToCompleteException
@@ -1556,6 +2143,9 @@
     JClassType b = to.getType("B");
 
     JTypeParameter syntheticTypeParam = new JTypeParameter("U", 0);
+    // Force the type parameter to have a declaring class
+    JClassType mockType = new JGenericType(to, a.getPackage(), null, false,
+        "C", false, new JTypeParameter[] {syntheticTypeParam});
     syntheticTypeParam.setBounds(makeArray(b));
 
     JParameterizedType parameterizedType = to.getParameterizedType(a,