Revision: 6119
Author: amitman...@google.com
Date: Thu Sep 10 16:26:01 2009
Log: Commiting the initial version of the HtmlUnit plugin. Many tests,  
including all
of EmulSuite, pass. Remaining work:
(i) Test with other GwtTests and fix.
(ii) Re-factor the code.
(iii) Garbage collection of javaObject references.
(iv) Minor todos like implementation of toString.

Patch by: amitmanjhi
Review (and some pair-programming) by: jat (TBR)


http://code.google.com/p/google-web-toolkit/source/detail?r=6119

Added:
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelClient.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/HostedModePluginObject.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ServerMethods.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java
   
/branches/farewellSwt/user/src/com/google/gwt/junit/RunStyleHtmlUnitHosted.java
Modified:
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
   
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java
  /branches/farewellSwt/eclipse/dev/oophm/.classpath
  /branches/farewellSwt/eclipse/user/.classpath
  /branches/farewellSwt/user/build.xml
  /branches/farewellSwt/user/src/com/google/gwt/junit/JUnitShell.java
  /branches/farewellSwt/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java

=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelClient.java
   
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import com.google.gwt.core.ext.TreeLogger;
+import  
com.google.gwt.dev.shell.BrowserChannel.SessionHandler.ExceptionOrReturnValue;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import java.io.IOException;
+import java.net.Socket;
+
+/**
+ * Implementation of the BrowserChannel for the client side.
+ *
+ */
+public class BrowserChannelClient extends BrowserChannel {
+
+  private static final int PROTOCOL_VERSION = 2;
+  private final HtmlUnitSessionHandler htmlUnitSessionHandler;
+  private final PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+  private final String moduleName;
+  private final String tabKey;
+  private final String sessionKey;
+  private final String url;
+  private final String versionString;
+  private boolean connected = false;
+
+  public BrowserChannelClient(String addressParts[], String url,
+      String sessionKey, String moduleName, String versionString,
+      HtmlUnitSessionHandler htmlUnitSessionHandler) throws IOException {
+    super(new Socket(addressParts[0], Integer.parseInt(addressParts[1])));
+    connected = true;
+    this.url = url;
+    this.sessionKey = sessionKey;
+    this.moduleName = moduleName;
+    this.tabKey = ""; // TODO(jat): update when tab support is added.
+    this.versionString = versionString;
+    logger.setMaxDetail(TreeLogger.WARN);
+    logger.log(TreeLogger.SPAM, "BrowserChannelClient, versionString: "
+        + versionString);
+    this.htmlUnitSessionHandler = htmlUnitSessionHandler;
+  }
+
+  public boolean disconnectFromHost() throws IOException {
+    logger.log(TreeLogger.DEBUG, "disconnecting channel " + this);
+    if (!isConnected()) {
+      logger.log(TreeLogger.DEBUG,
+          "Disconnecting already disconnected channel " + this);
+      return false;
+    }
+    new QuitMessage(this).send();
+    endSession();
+    connected = false;
+    return true;
+  }
+
+  public boolean isConnected() {
+    return connected;
+  }
+
+  // TODO (amitmanjhi): refer the state (message?) transition diagram
+  /**
+   * returns true iff execution completes normally.
+   */
+  public boolean process() throws IOException, BrowserChannelException {
+    if (!init()) {
+      disconnectFromHost();
+      return false;
+    }
+    logger.log(TreeLogger.DEBUG, "sending " + MessageType.LOAD_MODULE
+        + " message, userAgent: " + htmlUnitSessionHandler.getUserAgent());
+    ReturnMessage returnMessage = null;
+    synchronized (htmlUnitSessionHandler.getHtmlPage()) {
+      new LoadModuleMessage(this, url, tabKey, sessionKey, moduleName,
+          htmlUnitSessionHandler.getUserAgent()).send();
+      returnMessage = reactToMessages(htmlUnitSessionHandler, true);
+    }
+    logger.log(TreeLogger.DEBUG, "loaded module, returnValue: "
+        + returnMessage.getReturnValue() + ", isException: "
+        + returnMessage.isException());
+    return !returnMessage.isException();
+  }
+
+  public ReturnMessage reactToMessagesWhileWaitingForReturn(
+      HtmlUnitSessionHandler handler) throws IOException,
+      BrowserChannelException {
+    return reactToMessages(handler, true);
+  }
+
+  /*
+   * Perform the initial interaction. Return true if interaction succeeds,  
false
+   * if it fails. Do a check protocol versions, expected with 2.0+ oophm
+   * protocol.
+   */
+  private boolean init() throws IOException, BrowserChannelException {
+    logger.log(TreeLogger.DEBUG, "sending " + MessageType.CHECK_VERSIONS
+        + " message");
+    new CheckVersionsMessage(this, PROTOCOL_VERSION, PROTOCOL_VERSION,
+        versionString).send();
+    MessageType type = Message.readMessageType(getStreamFromOtherSide());
+    switch (type) {
+      case PROTOCOL_VERSION:
+        ProtocolVersionMessage protocolMessage =  
ProtocolVersionMessage.receive(this);
+        logger.log(TreeLogger.DEBUG, MessageType.PROTOCOL_VERSION
+            + ": protocol version = " +  
protocolMessage.getProtocolVersion());
+        // TODO(jat) : save selected protocol version when a range is  
supported.
+        break;
+      case FATAL_ERROR:
+        FatalErrorMessage errorMessage = FatalErrorMessage.receive(this);
+        logger.log(TreeLogger.ERROR, "Received FATAL_ERROR message "
+            + errorMessage.getError());
+        return false;
+      default:
+        return false;
+    }
+
+    return true;
+  }
+
+  private ReturnMessage reactToMessages(
+      HtmlUnitSessionHandler htmlUnitSessionHandler, boolean expectReturn)
+      throws IOException, BrowserChannelException {
+    while (true) {
+      ExceptionOrReturnValue returnValue;
+      MessageType type = Message.readMessageType(getStreamFromOtherSide());
+      logger.log(TreeLogger.INFO, "client: received " + type + ", thread: "
+          + Thread.currentThread().getName());
+      try {
+        switch (type) {
+          case INVOKE:
+            InvokeOnClientMessage invokeMessage =  
InvokeOnClientMessage.receive(this);
+            returnValue = htmlUnitSessionHandler.invoke(this,
+                invokeMessage.getThis(), invokeMessage.getMethodName(),
+                invokeMessage.getArgs());
+            htmlUnitSessionHandler.sendFreeValues(this);
+            new ReturnMessage(this, returnValue.isException(),
+                returnValue.getReturnValue()).send();
+            break;
+          case INVOKE_SPECIAL:
+            InvokeSpecialMessage invokeSpecialMessage =  
InvokeSpecialMessage.receive(this);
+            logger.log(TreeLogger.DEBUG, type + " message " + ", thisRef: "
+                + invokeSpecialMessage.getArgs());
+            returnValue = htmlUnitSessionHandler.invokeSpecial(this,
+                invokeSpecialMessage.getDispatchId(),
+                invokeSpecialMessage.getArgs());
+            htmlUnitSessionHandler.sendFreeValues(this);
+            new ReturnMessage(this, returnValue.isException(),
+                returnValue.getReturnValue()).send();
+            break;
+          case FREE_VALUE:
+            FreeMessage freeMessage = FreeMessage.receive(this);
+            logger.log(TreeLogger.DEBUG, type + " message "
+                + freeMessage.getIds());
+            htmlUnitSessionHandler.freeValue(this, freeMessage.getIds());
+            // no response
+            break;
+          case LOAD_JSNI:
+            LoadJsniMessage loadJsniMessage =  
LoadJsniMessage.receive(this);
+            String jsniString = loadJsniMessage.getJsni();
+            htmlUnitSessionHandler.loadJsni(this, jsniString);
+            // no response
+            break;
+          case RETURN:
+            if (!expectReturn) {
+              logger.log(TreeLogger.ERROR, "Received unexpected "
+                  + MessageType.RETURN);
+            }
+            return ReturnMessage.receive(this);
+          case QUIT:
+            if (expectReturn) {
+              logger.log(TreeLogger.ERROR, "Received " + MessageType.QUIT
+                  + " while waiting for return");
+            }
+            disconnectFromHost();
+            return null;
+          default:
+            logger.log(TreeLogger.ERROR, "Unkown messageType: " + type
+                + ", expectReturn: " + expectReturn);
+            disconnectFromHost();
+            return null;
+        }
+      } catch (Exception ex) {
+        logger.log(TreeLogger.ERROR, "Unknown exception" + ex);
+        ex.printStackTrace();
+      }
+    }
+  }
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/HostedModePluginObject.java
         
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import com.gargoylesoftware.htmlunit.javascript.host.Window;
+
+import net.sourceforge.htmlunit.corejs.javascript.Context;
+import net.sourceforge.htmlunit.corejs.javascript.Function;
+import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
+import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
+
+import java.io.IOException;
+
+/**
+ * HTMLUnit object that represents the hosted-mode plugin.
+ */
+public class HostedModePluginObject extends ScriptableObject {
+
+  /**
+   * Function object which implements the connect method on the hosted-mode
+   * plugin.
+   */
+  private class ConnectMethod extends ScriptableObject implements Function  
{
+
+    private static final long serialVersionUID = -8799481412144205779L;
+    private static final int EXPECTED_NUM_ARGS = 5;
+
+    public Object call(Context context, Scriptable scope, Scriptable  
thisObj,
+        Object[] args) {
+      // Allow extra arguments for forward compatibility
+      if (args.length < EXPECTED_NUM_ARGS) {
+        throw Context.reportRuntimeError("Bad number of parameters for  
function"
+            + " connect: expected "
+            + EXPECTED_NUM_ARGS
+            + ", got "
+            + args.length);
+      }
+      try {
+        /*
+         * connect arguments: url, sessionKey, ipAddress:port, moduleName,
+         * hostedHtmlVersion
+         */
+        return connect((String) args[0], (String) args[1], (String)  
args[2],
+            (String) args[3], (String) args[4]);
+      } catch (ClassCastException e) {
+        throw Context.reportRuntimeError("Incorrect parameter types for "
+            + " connect: expected String/String/String/String/String");
+      }
+    }
+
+    public Scriptable construct(Context context, Scriptable scope,  
Object[] args) {
+      throw Context.reportRuntimeError("Function connect can't be used as  
a "
+          + "constructor");
+    }
+
+    @Override
+    public String getClassName() {
+      return "function HostedModePluginObject.connect";
+    }
+  }
+
+  /**
+   * Function object which implements the init method on the hosted-mode  
plugin.
+   */
+  private class InitMethod extends ScriptableObject implements Function {
+
+    private static final long serialVersionUID = -8799481412144205779L;
+    private static final String VERSION = "2.0";
+
+    public Object call(Context context, Scriptable scope, Scriptable  
thisObj,
+        Object[] args) {
+      // Allow extra arguments for forward compatibility
+      if (args.length < 1) {
+        throw Context.reportRuntimeError("Bad number of parameters for  
function"
+            + " init: expected 1, got " + args.length);
+      }
+      try {
+        window = (Window) args[0];
+        // TODO (amitmanjhi): what checking needs to be done here for  
window?
+        return init(VERSION);
+      } catch (ClassCastException e) {
+        throw Context.reportRuntimeError("Incorrect parameter types for "
+            + " initt: expected String");
+      }
+    }
+
+    public Scriptable construct(Context context, Scriptable scope,  
Object[] args) {
+      throw Context.reportRuntimeError("Function init can't be used as a "
+          + "constructor");
+    }
+
+    @Override
+    public String getClassName() {
+      return "function HostedModePluginObject.init";
+    }
+  }
+
+  private static final long serialVersionUID = -1815031145376726799L;
+
+  private Scriptable connectMethod;
+  private Scriptable initMethod;
+  private Window window;
+
+  /**
+   * Initiate a hosted mode connection to the requested port and load the
+   * requested module.
+   *
+   * @param url the complete url
+   * @param sessionKey a length 16 string to identify a "session"
+   * @param address "host:port" or "ipAddress:port" to use for the OOPHM  
server
+   * @param module module name to load
+   * @param version version string
+   * @return true if the connection succeeds
+   */
+  public boolean connect(String url, String sessionKey, String address,
+      String module, String version) {
+    String addressParts[] = address.split(":");
+    if (addressParts.length < 2) {
+      return false;
+    }
+    // TODO: add whitelist and default-port support?
+    System.out.println("connect(url=" + url + ", sessionKey=" + sessionKey
+        + ", address=" + address + ", module=" + module + ", version="
+        + version + "), window=" + System.identityHashCode(window) + ")");
+
+    try {
+      HtmlUnitSessionHandler htmlUnitSessionHandler = new  
HtmlUnitSessionHandler(window);
+      BrowserChannelClient browserChannelClient = new BrowserChannelClient(
+          addressParts, url, sessionKey, module, version,
+          htmlUnitSessionHandler);
+      htmlUnitSessionHandler.setSessionData(new SessionData(
+          htmlUnitSessionHandler, browserChannelClient));
+      return browserChannelClient.process();
+    } catch (BrowserChannelException e) {
+      e.printStackTrace();
+      return false;
+    } catch (IOException e) {
+      e.printStackTrace();
+      return false;
+    }
+  }
+
+  @Override
+  public Object get(String name, Scriptable start) {
+    if ("connect".equals(name)) {
+      if (connectMethod == null) {
+        connectMethod = new ConnectMethod();
+      }
+      return connectMethod;
+    } else if ("init".equals(name)) {
+      if (initMethod == null) {
+        initMethod = new InitMethod();
+      }
+      return initMethod;
+    }
+    return NOT_FOUND;
+  }
+
+  @Override
+  public String getClassName() {
+    return "HostedModePluginObject";
+  }
+
+  /**
+   * Verify that the plugin can be initialized properly and supports the
+   * requested version.
+   *
+   * @param version hosted mode protocol version
+   * @return true if initialization succeeds, otherwise false
+   */
+  public boolean init(String version) {
+    // TODO: what needs to be done here?
+    return true;
+  }
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java
         
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.shell.BrowserChannel.JavaObjectRef;
+import com.google.gwt.dev.shell.BrowserChannel.JsObjectRef;
+import com.google.gwt.dev.shell.BrowserChannel.SessionHandler;
+import com.google.gwt.dev.shell.BrowserChannel.Value;
+import com.google.gwt.dev.shell.BrowserChannel.Value.ValueType;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import com.gargoylesoftware.htmlunit.ScriptResult;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
+import com.gargoylesoftware.htmlunit.javascript.host.Window;
+
+import net.sourceforge.htmlunit.corejs.javascript.Context;
+import net.sourceforge.htmlunit.corejs.javascript.Function;
+import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
+import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
+import net.sourceforge.htmlunit.corejs.javascript.Undefined;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Handle session tasks for HtmlUnit. TODO (amitmanjhi): refactor
+ * SessionHandler.
+ */
+public class HtmlUnitSessionHandler extends SessionHandler {
+
+  Map<Integer, JavaObject> javaObjectCache;
+  int nextId;
+
+  /**
+   * The htmlPage is also used to synchronize calls to Java code.
+   */
+  private HtmlPage htmlPage;
+
+  private Set<Integer> javaObjectsToFree;
+  private JavaScriptEngine jsEngine;
+  private IdentityHashMap<Scriptable, Integer> jsObjectToRef;
+
+  private final PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+
+  private int nextRefId = 1;
+  private Map<Integer, Scriptable> refToJsObject;
+
+  private SessionData sessionData;
+  private final Window window;
+
+  HtmlUnitSessionHandler(Window window) {
+    this.window = window;
+    logger.setMaxDetail(TreeLogger.ERROR);
+    jsEngine = this.window.getJavaScriptEngine();
+    htmlPage = (HtmlPage) this.window.getWebWindow().getEnclosedPage();
+    logger.log(TreeLogger.INFO, "jsEngine = " + jsEngine + ", HtmlPage = "
+        + htmlPage);
+
+    jsObjectToRef = new IdentityHashMap<Scriptable, Integer>();
+    javaObjectsToFree = new HashSet<Integer>();
+    nextRefId = 1;
+    refToJsObject = new HashMap<Integer, Scriptable>();
+
+    // related to JavaObject cache.
+    nextId = 1; // skipping zero, reserved.
+    javaObjectCache = new HashMap<Integer, JavaObject>();
+  }
+
+  @Override
+  public void freeValue(BrowserChannel channel, int[] ids) {
+    for (int id : ids) {
+      Scriptable scriptable = refToJsObject.remove(id);
+      if (scriptable != null) {
+        jsObjectToRef.remove(scriptable);
+      }
+    }
+  }
+
+  public HtmlPage getHtmlPage() {
+    return htmlPage;
+  }
+
+  public JavaObject getOrCreateJavaObject(int refId, Context context) {
+    JavaObject javaObject = javaObjectCache.get(refId);
+    if (javaObject == null) {
+      javaObject = new JavaObject(context, sessionData, nextId++);
+      javaObjectCache.put(refId, javaObject);
+    }
+    return javaObject;
+  }
+
+  @Override
+  public ExceptionOrReturnValue getProperty(BrowserChannel channel, int  
refId,
+      int dispId) {
+    throw new UnsupportedOperationException(
+        "getProperty should not be called on the client-side");
+  }
+
+  public String getUserAgent() {
+    return "HtmlUnit-"
+        + jsEngine.getWebClient().getBrowserVersion().getUserAgent();
+  }
+
+  @Override
+  public ExceptionOrReturnValue invoke(BrowserChannel channel, Value  
thisObj,
+      int dispId, Value[] args) {
+    throw new UnsupportedOperationException(
+        "should not be called on the client side");
+  }
+
+  public ExceptionOrReturnValue invoke(BrowserChannel channel, Value  
thisObj,
+      String methodName, Value[] args) {
+    logger.log(TreeLogger.DEBUG, "INVOKE: thisObj: " + thisObj
+        + ", methodName: " + methodName + ", args: " + args);
+    /*
+     * 1. lookup functions by name. 2. Find context and scope. 3. Convert
+     * thisObject to ScriptableObject 4. Convert args 5. Get return value
+     */
+    Context jsContext = Context.getCurrentContext();
+    ScriptableObject jsThis;
+    if (thisObj.getType() == ValueType.NULL) {
+      jsThis = (ScriptableObject) window;
+    } else {
+      jsThis = (ScriptableObject) makeJsvalFromValue(jsContext, thisObj);
+    }
+    Object functionObject = ScriptableObject.getProperty(
+        (ScriptableObject) window, methodName);
+    if (functionObject == ScriptableObject.NOT_FOUND) {
+      logger.log(TreeLogger.ERROR, "function " + methodName
+          + " NOT FOUND, thisObj: " + jsThis + ", methodName: " +  
methodName);
+      // TODO: see if this maps to QUIT
+      return new ExceptionOrReturnValue(true, new Value(null));
+    }
+    Function jsFunction = (Function) functionObject;
+    logger.log(TreeLogger.SPAM, "INVOKE: jsFunction: " + jsFunction);
+
+    Object jsArgs[] = new Object[args.length];
+    for (int i = 0; i < args.length; i++) {
+      jsArgs[i] = makeJsvalFromValue(jsContext, args[i]);
+    }
+    Object result = null;
+    try {
+      result = jsEngine.callFunction(htmlPage, jsFunction, jsContext,  
window,
+          jsThis, jsArgs);
+    } catch (Exception ex) {
+      logger.log(TreeLogger.ERROR, "INVOKE: exception " + ex + ",  
message: "
+          + ex.getMessage() + " when invoking " + methodName);
+      return new ExceptionOrReturnValue(true, makeValueFromJsval(jsContext,
+          Undefined.instance));
+    }
+    logger.log(TreeLogger.INFO, "INVOKE: result: " + result
+        + " of jsFunction: " + jsFunction);
+    return new ExceptionOrReturnValue(false, makeValueFromJsval(jsContext,
+        result));
+  }
+
+  public ExceptionOrReturnValue invokeSpecial(BrowserChannel channel,
+      SpecialDispatchId specialDispatchId, Value[] args) {
+    throw new UnsupportedOperationException(
+        "InvokeSpecial must not be called on the client side");
+  }
+
+  public void loadJsni(BrowserChannel channel, String jsniString) {
+    logger.log(TreeLogger.SPAM, "LOAD_JSNI: " + jsniString);
+    ScriptResult scriptResult = htmlPage.executeJavaScript(jsniString);
+    logger.log(TreeLogger.INFO, "LOAD_JSNI: scriptResult=" + scriptResult);
+  }
+
+  @Override
+  public TreeLogger loadModule(TreeLogger logger, BrowserChannel channel,
+      String moduleName, String userAgent, String url, String tabKey,
+      String sessionKey) {
+    throw new UnsupportedOperationException("loadModule must not be  
called");
+  }
+
+  public Value makeValueFromJsval(Context jsContext, Object value) {
+    if (value == Undefined.instance) {
+      return new Value();
+    }
+    if (value instanceof JavaObject) {
+      Value returnVal = new Value();
+      int refId = ((JavaObject) value).getRefId();
+      returnVal.setJavaObject(new JavaObjectRef(refId));
+      return returnVal;
+    }
+    if (value instanceof Scriptable) {
+      Integer refId = jsObjectToRef.get((Scriptable) value);
+      if (refId == null) {
+        refId = nextRefId++;
+        jsObjectToRef.put((Scriptable) value, refId);
+        refToJsObject.put(refId, (Scriptable) value);
+      }
+      Value returnVal = new Value();
+      returnVal.setJsObject(new JsObjectRef(refId));
+      return returnVal;
+    }
+    return new Value(value);
+  }
+
+  // TODO: check synchronization and multi-threading
+  public void sendFreeValues(BrowserChannel channel) {
+    int size = javaObjectsToFree.size();
+    if (size == 0) {
+      return;
+    }
+    int ids[] = new int[size];
+    int index = 0;
+    for (int id : javaObjectsToFree) {
+      ids[index++] = id;
+    }
+    if (ServerMethods.freeJava(channel, this, ids)) {
+      javaObjectsToFree.clear();
+    }
+  }
+
+  @Override
+  public ExceptionOrReturnValue setProperty(BrowserChannel channel, int  
refId,
+      int dispId, Value newValue) {
+    throw new UnsupportedOperationException(
+        "setProperty should not be called on the client-side");
+  }
+
+  public void setSessionData(SessionData sessionData) {
+    this.sessionData = sessionData;
+  }
+
+  @Override
+  public void unloadModule(BrowserChannel channel, String moduleName) {
+    throw new UnsupportedOperationException("unloadModule must not be  
called");
+  }
+
+  /*
+   * Returning java objects works. No need to return NativeNumber,  
NativeString,
+   * NativeBoolean, or Undefined.
+   */
+  Object makeJsvalFromValue(Context jsContext, Value value) {
+    switch (value.getType()) {
+      case NULL:
+        return null;
+      case BOOLEAN:
+        if (value.getBoolean()) {
+          return Boolean.TRUE;
+        }
+        return Boolean.FALSE;
+      case BYTE:
+        return new Byte(value.getByte());
+      case CHAR:
+        return new Character(value.getChar());
+      case SHORT:
+        return new Short(value.getShort());
+      case INT:
+        return new Integer(value.getInt());
+      case FLOAT:
+        return new Float(value.getFloat());
+      case DOUBLE:
+        return new Double(value.getDouble());
+      case STRING:
+        return value.getString();
+      case JAVA_OBJECT:
+        JavaObjectRef javaRef = value.getJavaObject();
+        return JavaObject.getOrCreateJavaObject(javaRef, sessionData,  
jsContext);
+      case JS_OBJECT:
+        Scriptable scriptable =  
refToJsObject.get(value.getJsObject().getRefid());
+        assert scriptable != null;
+        return scriptable;
+      case UNDEFINED:
+        return Undefined.instance;
+    }
+    return null;
+  }
+
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java    
 
Thu Sep 10 16:26:01 2009
@@ -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.dev.shell;
+
+import com.google.gwt.dev.shell.BrowserChannel.InvokeOnServerMessage;
+import com.google.gwt.dev.shell.BrowserChannel.JavaObjectRef;
+import com.google.gwt.dev.shell.BrowserChannel.ReturnMessage;
+import com.google.gwt.dev.shell.BrowserChannel.Value;
+
+import net.sourceforge.htmlunit.corejs.javascript.Context;
+import net.sourceforge.htmlunit.corejs.javascript.Function;
+import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
+import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
+import net.sourceforge.htmlunit.corejs.javascript.Undefined;
+
+import java.io.IOException;
+
+/**
+ * Class to encapsulate JavaObject on the server side.
+ */
+public class JavaObject extends ScriptableObject implements Function {
+
+  private static final long serialVersionUID = -7923090130737830902L;
+
+  public static JavaObject getOrCreateJavaObject(JavaObjectRef javaRef,
+      SessionData sessionData, Context context) {
+    return sessionData.getSessionHandler().getOrCreateJavaObject(
+        javaRef.getRefid(), context);
+  }
+
+  static boolean isJavaObject(Context jsContext, ScriptableObject  
javaObject) {
+    return javaObject instanceof JavaObject;
+  }
+
+  private Context jsContext;
+
+  private final int objectRef;
+
+  private final SessionData sessionData;
+
+  public JavaObject(Context jsContext, SessionData sessionData, int  
objectRef) {
+    this.objectRef = objectRef;
+    this.sessionData = sessionData;
+    this.jsContext = jsContext;
+  }
+
+  /*
+   * If this function fails for any reason, we return Undefined instead of
+   * throwing an Exception in all cases except when Java throws an  
Exception.
+   */
+  public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+      Object[] args) {
+
+    if (args.length < 2) {
+      return Undefined.instance;
+    }
+    Value valueArgs[] = new Value[args.length - 2];
+    for (int i = 0; i < valueArgs.length; i++) {
+      valueArgs[i] = sessionData.getSessionHandler().makeValueFromJsval(cx,
+          args[i + 2]);
+    }
+
+    /**
+     * Called when the JavaObject is invoked as a function. We ignore the
+     * thisObj argument, which is usually the window object.
+     *
+     * Returns a JS array, with the first element being a boolean  
indicating
+     * that an exception occured, and the second element is either the  
return
+     * value or the exception which was thrown. In this case, we always  
return
+     * false and raise the exception ourselves.
+     */
+
+    Value thisValue =  
sessionData.getSessionHandler().makeValueFromJsval(cx,
+        args[1]);
+    int dispatchId = ((Number) args[0]).intValue();
+
+    ReturnMessage returnMessage = null;
+    synchronized (sessionData.getSessionHandler().getHtmlPage()) {
+      try {
+        new InvokeOnServerMessage(sessionData.getChannel(), dispatchId,
+            thisValue, valueArgs).send();
+      } catch (IOException e) {
+        return Undefined.instance;
+      }
+      try {
+        returnMessage = ((BrowserChannelClient)  
sessionData.getChannel()).reactToMessagesWhileWaitingForReturn(sessionData.getSessionHandler());
+      } catch (IOException e) {
+        return Undefined.instance;
+      } catch (BrowserChannelException e) {
+        return Undefined.instance;
+      }
+    }
+    Value returnValue = returnMessage.getReturnValue();
+    if (returnMessage.isException()) {
+      throw new RuntimeException("JavaObject.call failed, returnMessage: "
+          + returnValue.toString());
+    }
+    /*
+     * Return a object array ret. ret[0] is a boolean indicating whether an
+     * exception was thrown or not. ret[1] is the exception or the return  
value.
+     */
+    Object ret[] = new Object[2];
+    ret[0] = Boolean.FALSE;
+    ret[1] = sessionData.getSessionHandler().makeJsvalFromValue(cx,  
returnValue);
+    return ret;
+  }
+
+  public Scriptable construct(Context cx, Scriptable scope, Object[] args)  
{
+    throw Context.reportRuntimeError("JavaObject can't be used as a "
+        + "constructor");
+  }
+
+  // ignoring the 'start' argument.
+  @Override
+  public Object get(String name, Scriptable start) {
+    if ("toString".equals(name)) {
+      return sessionData.getToStringTearOff();
+    }
+    if ("id".equals(name)) {
+      return objectRef;
+    }
+    if ("__noSuchMethod__".equals(name)) {
+      return Undefined.instance;
+    }
+    System.err.println("Unknown property name in get " + name);
+    return Undefined.instance;
+  }
+
+  // ignoring the 'start' argument.
+  @Override
+  public Object get(int index, Scriptable start) {
+    Value value = ServerMethods.getProperty(sessionData.getChannel(),
+        sessionData.getSessionHandler(), objectRef, index);
+    return sessionData.getSessionHandler().makeJsvalFromValue(jsContext,  
value);
+  }
+
+  @Override
+  public String getClassName() {
+    return "Class JavaObject";
+  }
+
+  @Override
+  public void put(int dispatchId, Scriptable start, Object value) {
+    HtmlUnitSessionHandler sessionHandler =  
sessionData.getSessionHandler();
+    if (!ServerMethods.setProperty(sessionData.getChannel(),  
sessionHandler,
+        objectRef, dispatchId, sessionHandler.makeValueFromJsval(jsContext,
+            value))) {
+      // TODO: fix later.
+      throw new RuntimeException("setProperty failed");
+    }
+  }
+
+  int getRefId() {
+    return objectRef;
+  }
+
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ServerMethods.java 
 
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import com.google.gwt.dev.shell.BrowserChannel.FreeMessage;
+import com.google.gwt.dev.shell.BrowserChannel.InvokeSpecialMessage;
+import com.google.gwt.dev.shell.BrowserChannel.ReturnMessage;
+import com.google.gwt.dev.shell.BrowserChannel.Value;
+import  
com.google.gwt.dev.shell.BrowserChannel.SessionHandler.SpecialDispatchId;
+
+import java.io.IOException;
+
+/**
+ * A class to encapsulate function invocations of objects on the server  
side.
+ */
+public class ServerMethods {
+  /**
+   * Tell the server that the client no longer has any references to the
+   * specified Java object.
+   *
+   * @param ids ID of object to free
+   * @return false if an error occurred
+   */
+  static boolean freeJava(BrowserChannel channel,
+      HtmlUnitSessionHandler handler, int ids[]) {
+    if (!((BrowserChannelClient) channel).isConnected()) {
+      // ignoring freeJava after disconnect.
+      return true;
+    }
+    try {
+      new FreeMessage(channel, ids).send();
+    } catch (IOException e) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Get the value of a property on an object.
+   *
+   * @param objectRef ID of object to fetch field on
+   * @param dispatchId dispatch ID of field
+   * @return the value of the property, undef if none (or on error)
+   */
+  static Value getProperty(BrowserChannel channel,
+      HtmlUnitSessionHandler handler, int objectRef, int dispatchId) {
+    if (!((BrowserChannelClient) channel).isConnected()) {
+      // ignoring getProperty() after disconnect
+      return new Value();
+    }
+    Value args[] = new Value[2];
+    args[0] = new Value();
+    args[0].setInt(objectRef);
+    args[1] = new Value();
+    args[1].setInt(dispatchId);
+
+    synchronized (handler.getHtmlPage()) {
+      try {
+        new InvokeSpecialMessage(channel, SpecialDispatchId.GetProperty,  
args).send();
+        // TODO: refactor in order to remove cast.
+        ReturnMessage returnMessage = ((BrowserChannelClient)  
channel).reactToMessagesWhileWaitingForReturn(handler);
+        if (!returnMessage.isException()) {
+          return returnMessage.getReturnValue();
+        }
+      } catch (IOException e) {
+      } catch (BrowserChannelException e) {
+      }
+    }
+    return new Value();
+  }
+
+  /**
+   * Set the value of a property on an object.
+   *
+   * @param objectRef ID of object to fetch field on
+   * @param dispatchId dispatch ID of field
+   * @param value value to store in the property
+   * @return false if an error occurred
+   */
+  static boolean setProperty(BrowserChannel channel,
+      HtmlUnitSessionHandler handler, int objectRef, int dispatchId, Value  
value) {
+    Value args[] = new Value[3];
+    for (int i = 0; i < args.length; i++) {
+      args[i] = new Value();
+    }
+    args[0].setInt(objectRef);
+    args[1].setInt(dispatchId);
+    args[2] = value;
+    synchronized (handler.getHtmlPage()) {
+      try {
+        new InvokeSpecialMessage(channel, SpecialDispatchId.SetProperty,  
args).send();
+        ReturnMessage returnMessage = ((BrowserChannelClient)  
channel).reactToMessagesWhileWaitingForReturn(handler);
+        if (!returnMessage.isException()) {
+          return true;
+        }
+      } catch (IOException e) {
+      } catch (BrowserChannelException e) {
+      }
+    }
+    // TODO: use the returned exception?
+    return false;
+  }
+
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java   
 
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+/**
+ * A class to encapsulate data needed by {...@code JavaObject}.
+ */
+public class SessionData {
+  private final BrowserChannel browserChannel;
+  private final HtmlUnitSessionHandler sessionHandler;
+
+  public SessionData(HtmlUnitSessionHandler sessionHandler, BrowserChannel  
browserChannel) {
+    this.sessionHandler = sessionHandler;
+    this.browserChannel = browserChannel;
+  }
+
+  public BrowserChannel getChannel() {
+    return browserChannel;
+  }
+
+  public HtmlUnitSessionHandler getSessionHandler() {
+    return sessionHandler;
+  }
+
+  public Object getToStringTearOff() {
+    // TODO Auto-generated method stub
+    return null;
+  }
+}
=======================================
--- /dev/null
+++  
/branches/farewellSwt/user/src/com/google/gwt/junit/RunStyleHtmlUnitHosted.java 
 
Thu Sep 10 16:26:01 2009
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.junit;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.shell.HostedModePluginObject;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import com.gargoylesoftware.htmlunit.BrowserVersion;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebWindow;
+import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
+import com.gargoylesoftware.htmlunit.javascript.host.Window;
+
+import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
+
+/**
+ * Runstyle for HTMLUnit in hosted mode.
+ */
+public class RunStyleHtmlUnitHosted extends RunStyleHtmlUnit {
+
+  /**
+   * Run HMTLUnit in a separate thread, replacing the default  
JavaScriptEngine
+   * with one that has the necessary hosted mode hooks.
+   */
+  protected static class HtmlUnitHostedThread extends HtmlUnitThread {
+
+    public HtmlUnitHostedThread(BrowserVersion browser, String url,
+        TreeLogger treeLogger) {
+      super(browser, url, treeLogger);
+    }
+
+    @Override
+    protected void setupWebClient(WebClient webClient) {
+      JavaScriptEngine hostedEngine = new  
HostedJavaScriptEngine(webClient);
+      webClient.setJavaScriptEngine(hostedEngine);
+    }
+  }
+
+  /**
+   * JavaScriptEngine subclass that provides a hook of initializing the
+   * __gwt_HostedModePlugin property on any new window, so it acts just  
like
+   * Firefox with the XPCOM plugin installed.
+   */
+  private static class HostedJavaScriptEngine extends JavaScriptEngine {
+
+    private static final long serialVersionUID = 3594816610842448691L;
+
+    public HostedJavaScriptEngine(WebClient webClient) {
+      super(webClient);
+    }
+
+    @Override
+    public void initialize(WebWindow webWindow) {
+      // Hook in the hosted-mode plugin after initializing the JS engine.
+      super.initialize(webWindow);
+      Window window = (Window) webWindow.getScriptObject();
+      window.defineProperty("__gwt_HostedModePlugin",
+          new HostedModePluginObject(), ScriptableObject.READONLY);
+    }
+  }
+
+  public static HtmlUnitThread createHtmlUnitThread(BrowserVersion browser,
+      String url, TreeLogger treeLogger) {
+    return new HtmlUnitHostedThread(browser, url, treeLogger);
+  }
+
+  public static void startHtmlUnitThread(String url) {
+    PrintWriterTreeLogger pw = new PrintWriterTreeLogger();
+    // TODO(amitmanjhi): get the correct browser emulation
+    HtmlUnitThread thread = createHtmlUnitThread(
+        BrowserVersion.FIREFOX_3, url, pw);
+    thread.start();
+  }
+
+  public RunStyleHtmlUnitHosted(JUnitShell unitShell, String[] targets) {
+    super(unitShell, targets);
+  }
+
+  @Override
+  public void maybeCompileModule(String moduleName) {
+    // No compilation needed for hosted mode
+  }
+
+  @Override
+  protected HtmlUnitThread createHtmlUnitThread(BrowserVersion browser,
+      String url) {
+    return RunStyleHtmlUnitHosted.createHtmlUnitThread(browser, url,
+        shell.getTopLogger());
+  }
+
+  @Override
+  protected String getMyUrl(String moduleName) {
+    // TODO(jat): get the correct address/port
+    return super.getMyUrl(moduleName) + "?gwt.hosted=localhost:9997";
+  }
+}
=======================================
---  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
         
Mon Aug 10 16:15:14 2009
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java
         
Thu Sep 10 16:26:01 2009
@@ -69,11 +69,16 @@
     * Class representing a reference to a JS object.
     */
    public static class JsObjectRef {
-    private int refId;
-
+
+    // TODO: refactor and remove this method.
+    public static void checkIdMap(int refId) {
+      assert !JSOBJECT_ID_MAP.get().containsKey(refId)
+      || (JSOBJECT_ID_MAP.get().get(refId).get() == null);
+    }
+
+    private int refId;
+
      public JsObjectRef(int refId) {
-      assert !JSOBJECT_ID_MAP.get().containsKey(refId)
-          || (JSOBJECT_ID_MAP.get().get(refId).get() == null);
        this.refId = refId;
      }

@@ -1325,6 +1330,7 @@
        }
      }

+    JsObjectRef.checkIdMap(refId);
      JsObjectRef toReturn = new JsObjectRef(refId);
      Reference<JsObjectRef> ref = new WeakReference<JsObjectRef>(toReturn,
          JSOBJECT_REF_QUEUE.get());
=======================================
---  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java  
 
Thu Jan  8 16:13:53 2009
+++  
/branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java  
 
Thu Sep 10 16:26:01 2009
@@ -144,6 +144,7 @@
     * @param jsRefId pointer to underlying JsRootedValue as an integer.
     */
    public JsValueOOPHM(int jsRefId) {
+    JsObjectRef.checkIdMap(jsRefId);
      this.value = new JsObjectRef(jsRefId);
    }

=======================================
--- /branches/farewellSwt/eclipse/dev/oophm/.classpath  Wed Mar 11 12:33:27  
2009
+++ /branches/farewellSwt/eclipse/dev/oophm/.classpath  Thu Sep 10 16:26:01  
2009
@@ -4,6 +4,8 @@
        <classpathentry kind="src" path="oophm/overlay"/>
        <classpathentry exported="true" kind="con"  
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry exported="true" kind="var"  
path="GWT_TOOLS/lib/sun/swingworker/swing-worker-1.1.jar"  
sourcepath="/GWT_TOOLS/lib/sun/swingworker/swing-worker-1.1-src.zip"/>
+       <classpathentry kind="var"  
path="GWT_TOOLS/lib/htmlunit/htmlunit-core-js-2.5.jar"  
sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-core-js-2.5-sources.jar"/>
+       <classpathentry kind="var" 
path="GWT_TOOLS/lib/htmlunit/htmlunit-2.5.jar"  
sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-2.5-sources.jar"/>
        <classpathentry combineaccessrules="false" kind="src"  
path="/gwt-dev-windows"/>
        <classpathentry kind="output" path="bin"/>
  </classpath>
=======================================
--- /branches/farewellSwt/eclipse/user/.classpath       Thu Aug 20 15:05:36 2009
+++ /branches/farewellSwt/eclipse/user/.classpath       Thu Sep 10 16:26:01 2009
@@ -24,6 +24,7 @@
        <classpathentry kind="var"  
path="GWT_TOOLS/lib/xerces/xerces-2_9_1/xml-apis.jar" />
        <classpathentry kind="var" path="GWT_TOOLS/lib/w3c/sac/sac-1.3.jar"/>
        <classpathentry kind="var" 
path="GWT_TOOLS/lib/w3c/flute/flute-1.3.jar"/>
+       <classpathentry combineaccessrules="false" kind="src"  
path="/gwt-dev-oophm"/>
        <classpathentry combineaccessrules="false" kind="src"  
path="/gwt-dev-windows"/>
        <classpathentry kind="output" path="bin"/>
  </classpath>
=======================================
--- /branches/farewellSwt/user/build.xml        Thu Sep  3 10:59:12 2009
+++ /branches/farewellSwt/user/build.xml        Thu Sep 10 16:26:01 2009
@@ -49,6 +49,7 @@
          <pathelement  
location="${gwt.tools.lib}/selenium/selenium-java-client-driver.jar" />
          <pathelement location="${gwt.tools.lib}/w3c/sac/sac-1.3.jar" />
          <pathelement location="${gwt.tools.lib}/w3c/flute/flute-1.3.jar" />
+        <pathelement location="${gwt.build.lib}/gwt-dev-oophm.jar" />
          <pathelement location="${gwt.dev.jar}" />
        </classpath>
      </gwt.javac>
=======================================
--- /branches/farewellSwt/user/src/com/google/gwt/junit/JUnitShell.java Mon  
Aug 17 09:47:12 2009
+++ /branches/farewellSwt/user/src/com/google/gwt/junit/JUnitShell.java Thu  
Sep 10 16:26:01 2009
@@ -43,6 +43,7 @@
  import junit.framework.TestCase;
  import junit.framework.TestResult;

+import java.awt.GraphicsEnvironment;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Arrays;
@@ -213,6 +214,32 @@
          }
        });

+      registerHandler(new ArgHandlerString() {
+        @Override
+        public String getPurpose() {
+          return "Runs hosted mode via HTMLUnit given a list of browsers; "
+              + "e.g. IE6,IE7,FF2,FF3...";
+        }
+
+        @Override
+        public String getTag() {
+          return "-htmlunithosted";
+        }
+
+        @Override
+        public String[] getTagArgs() {
+          return new String[] {"browserNames"};
+        }
+
+        @Override
+        public boolean setString(String str) {
+          String[] targets = str.split(",");
+          runStyle = new RunStyleHtmlUnitHosted(JUnitShell.this, targets);
+          numClients = ((RunStyleHtmlUnit) runStyle).numBrowsers();
+          return runStyle != null;
+        }
+      });
+
        registerHandler(new ArgHandlerString() {
          @Override
          public String getPurpose() {
@@ -400,7 +427,7 @@
     * begin running the test. "Contacted" does not necessarily mean "the  
test has
     * begun," e.g. for linker errors stopping the test initialization.
     */
-  private static final int TEST_BEGIN_TIMEOUT_MILLIS = 60000;
+  private static final int TEST_BEGIN_TIMEOUT_MILLIS = 6000000;

    /**
     * The amount of time to wait for all clients to complete a single test
@@ -590,7 +617,8 @@
     */
    private JUnitShell() {
      setRunTomcat(true);
-    setHeadless(true);
+    setHeadless(false);
+    setHeadless(GraphicsEnvironment.isHeadless());

      // Legacy: -Dgwt.hybrid runs web mode
      if (System.getProperty(PROP_JUNIT_HYBRID_MODE) != null) {
@@ -619,7 +647,8 @@
    protected void initializeLogger() {
      if (isHeadless()) {
        consoleLogger = new PrintWriterTreeLogger();
-      consoleLogger.setMaxDetail(getCompilerOptions().getLogLevel());
+      // TODO (amitmanjhi): GwtShell overlay fix.
+      consoleLogger.setMaxDetail(TreeLogger.INFO);
      } else {
        super.initializeLogger();
      }
@@ -683,19 +712,10 @@
      return !messageQueue.hasResult();
    }

-  @Override
+  // TODO (amitmanjhi): GwtShell overlay fix, removed Override.
    protected boolean shouldAutoGenerateResources() {
      return runStyle.shouldAutoGenerateResources();
    }
-
-  @Override
-  protected void sleep() {
-    if (runStyle.isLocal()) {
-      super.sleep();
-    } else {
-      messageQueue.waitForResults(1000);
-    }
-  }

    void compileForWebMode(String moduleName, String... userAgents)
        throws UnableToCompleteException {
@@ -881,7 +901,9 @@
        testBeginTime = System.currentTimeMillis();
        testBeginTimeout = testBeginTime + TEST_BEGIN_TIMEOUT_MILLIS;
        testMethodTimeout = 0; // wait until test execution begins
-      pumpEventLoop();
+      while (notDone()) {
+        messageQueue.waitForResults(1000);
+      }
      } catch (TimeoutException e) {
        lastLaunchFailed = true;
        testResult.addError(testCase, e);
=======================================
---  
/branches/farewellSwt/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java       
 
Wed Aug 12 17:20:08 2009
+++  
/branches/farewellSwt/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java       
 
Thu Sep 10 16:26:01 2009
@@ -58,6 +58,7 @@
        this.browser = browser;
        this.url = url;
        this.treeLogger = treeLogger;
+      this.setName("htmlUnit client thread");
      }

      public void handleAlert(Page page, String message) {
@@ -93,7 +94,7 @@
          // TODO(jat): is this necessary?
          webClient.waitForBackgroundJavaScriptStartingBefore(2000);
          page.getEnclosingWindow().getJobManager().waitForJobs(60000);
-        treeLogger.log(TreeLogger.DEBUG, "getPage returned "
+        treeLogger.log(TreeLogger.SPAM, "getPage returned "
              + ((HtmlPage) page).asXml());
          // TODO(amitmanjhi): call webClient.closeAllWindows()
        } catch (FailingHttpStatusCodeException e) {
@@ -182,7 +183,8 @@

    protected HtmlUnitThread createHtmlUnitThread(BrowserVersion browser,
        String url) {
-    return new HtmlUnitThread(browser, url, shell.getTopLogger());
+    return new HtmlUnitThread(browser, url, shell.getTopLogger().branch(
+        TreeLogger.SPAM, "logging for HtmlUnit thread"));
    }

    private Set<BrowserVersion> getBrowserSet(String[] targetsIn) {

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to