Revert "Re-working @UiField and Widget replacement to use DOM walking rather then IDs."

This reverts svn r7816, because its handling of tables isn't yet
robust enough. It will be back.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7858 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/config/gwt20_21userApi.conf b/tools/api-checker/config/gwt20_21userApi.conf
index 399a5fb..1959dcd 100644
--- a/tools/api-checker/config/gwt20_21userApi.conf
+++ b/tools/api-checker/config/gwt20_21userApi.conf
@@ -43,7 +43,6 @@
 :com/google/gwt/rpc/client/impl/ClientWriterFactory.java\
 :com/google/gwt/rpc/client/impl/EscapeUtil.java\
 :com/google/gwt/rpc/linker/*.java\
-:com/google/gwt/uibinder/client/UiBinderUtil.java\
 :com/google/gwt/uibinder/attributeparsers/*.java\
 :com/google/gwt/uibinder/elementparsers/*.java\
 :com/google/gwt/uibinder/testing/*.java\
diff --git a/user/src/com/google/gwt/dom/client/DOMImpl.java b/user/src/com/google/gwt/dom/client/DOMImpl.java
index da3117a..dc61ea2 100644
--- a/user/src/com/google/gwt/dom/client/DOMImpl.java
+++ b/user/src/com/google/gwt/dom/client/DOMImpl.java
@@ -240,23 +240,6 @@
     return node.nodeType;
   }-*/;
 
-  /**
-   * Get the child of a parent ignoring all text nodes that might be children of the parent.
-   * Returns null if the index is out of bounds.
-   */
-  public Node getNonTextChild(Node parent, int index) {
-    int i = 0;    
-    for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {     
-      if (child.getNodeType() == Node.ELEMENT_NODE) {
-        if (i == index) {
-          return child.cast();
-        }
-        i++;
-      }
-    }
-    return null;
-  }
-  
   public native Element getParentElement(Node node) /*-{
     var parent = node.parentNode;
     if (!parent || parent.nodeType != 1) {
@@ -378,6 +361,4 @@
   public native String toString(Element elem) /*-{
     return elem.outerHTML;
   }-*/;
-  
- 
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE6.java b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
index 2f75ce9..843f931 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplIE6.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
@@ -24,8 +24,6 @@
   private static boolean isIE6;
   private static boolean isIE6Detected;
 
-  
-  
   /**
    * Check if the browser is IE6 or IE7.
    * 
@@ -86,11 +84,6 @@
         / getZoomMultiple(doc) + doc.getScrollTop());
   }
 
-  @Override 
-  public native Node getNonTextChild(Node parent, int index) /*-{
-    return parent.children[index];
-  }-*/;
-  
   @Override
   public int getScrollLeft(Element elem) {
     if (isRTL(elem)) {
diff --git a/user/src/com/google/gwt/dom/client/Element.java b/user/src/com/google/gwt/dom/client/Element.java
index b157a94..d3b3692 100644
--- a/user/src/com/google/gwt/dom/client/Element.java
+++ b/user/src/com/google/gwt/dom/client/Element.java
@@ -719,6 +719,4 @@
      // on some browsers.
      this.title = title || '';
    }-*/;
-  
-  
 }
diff --git a/user/src/com/google/gwt/dom/client/Node.java b/user/src/com/google/gwt/dom/client/Node.java
index aea2ee1..44ff0c8 100644
--- a/user/src/com/google/gwt/dom/client/Node.java
+++ b/user/src/com/google/gwt/dom/client/Node.java
@@ -168,16 +168,6 @@
   }-*/;
 
   /**
-   * Get the child by index ignoring all text node children.
-   * Returns null if the index is out of bounds.
-   * 
-   * @param index The child index ignoring text node children
-   */
-  public final Node getNonTextChild(int index) {
-    return DOMImpl.impl.getNonTextChild(this, index);
-  }
-  
-  /**
    * The Document object associated with this node. This is also the
    * {@link Document} object used to create new nodes.
    */
@@ -193,7 +183,7 @@
   public final Element getParentElement() {
     return DOMImpl.impl.getParentElement(this);
   }
-  
+
   /**
    * The parent of this node. All nodes except Document may have a parent.
    * However, if a node has just been created and not yet added to the tree, or
diff --git a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
index b6487f3..4b8c1ed 100644
--- a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
+++ b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
@@ -18,14 +18,67 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.Node;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.UIObject;
 
 /**
  * Static helper methods used by UiBinder. These methods are likely to move,
  * so please don't use them for non-UiBinder code.
  */
 public class UiBinderUtil {
+  /**
+   * Temporary attachment record that keeps track of where an element was
+   * before attachment.  Use the detach method to put things back.
+   *
+   */
+  public static class TempAttachment {
+    private final Element element;
+    private final Element origParent;
+    private final Element origSibling;    
+    
+    private TempAttachment(Element origParent, Element origSibling, 
+        Element element) {
+      this.origParent = origParent;
+      this.origSibling = origSibling;
+      this.element = element;
+    }
+    
+    /**
+     * Restore to previous DOM state before attachment.
+     */
+    public void detach() {
+      // Put the panel's element back where it was.
+      if (origParent != null) {
+        origParent.insertBefore(element, origSibling);
+      } else {
+        orphan(element);
+      }
+    }
+  }  
   
   private static Element hiddenDiv;
+  
+  /**
+   * Attaches the element to the dom temporarily.  Keeps track of where it is 
+   * attached so that things can be put back latter.
+   * 
+   * @return attachment record which can be used for reverting back to previous
+   *         DOM state
+   */
+  public static TempAttachment attachToDom(Element element) {
+    // TODO(rjrjr) This is copied from HTMLPanel. Reconcile
+    ensureHiddenDiv();
+    
+    // Hang on to the panel's original parent and sibling elements so that it
+    // can be replaced.
+    Element origParent = element.getParentElement();
+    Element origSibling = element.getNextSiblingElement();
+
+    // Attach the panel's element to the hidden div.
+    hiddenDiv.appendChild(element);
+    
+    return new TempAttachment(origParent, origSibling, element);
+  }
 
   public static Element fromHtml(String html) {
     ensureHiddenDiv();
@@ -34,57 +87,13 @@
     orphan(newbie);
     return newbie;
   }
-  
-  public static Node getChild(Node node, int child) {
-    return node.getChild(child);
-  }
-  
-  public static Node getNonTextChild(Node node, int child) {
-    return node.getNonTextChild(child);
-  }
-  
-  public static Node getTableChild(Node node, int child) {
-    // If the table, has a tbody inside...
-    Element table = (Element)node;
-    Element firstChild = table.getNonTextChild(0).cast();
-    if ("tbody".equalsIgnoreCase(firstChild.getTagName())) {
-      return firstChild.getNonTextChild(child);
-    } else {
-      return table.getNonTextChild(child);
-    }
-  }
-  
-  public static native Element lookupNodeByTreeIndicies(Element parent, String query,
-      String xpath) /*-{
-    if (parent.querySelector) {
-      return parent.querySelector(query);
-    } else {
-      return parent.ownerDocument.evaluate(
-        xpath, parent, null, XPathResult.ANY_TYPE, null).iterateNext();
-    }
-  }-*/;
-  
-  public static native Element lookupNodeByTreeIndiciesIE(Element parent, int[] indicies) /*-{
-  var currentNode = parent;
-  for(var i = 0; i < indicies.length; i = i + 1) {
-    currentNode = currentNode.children[indicies[i]];
-  }
-  return currentNode;
-}-*/;
-  
-  public static native Element lookupNodeByTreeIndiciesUsingQuery(Element parent, String query) /*-{
-    return parent.querySelector(query);
-  }-*/; 
-  
-  public static native Element lookupNodeByTreeIndiciesUsingXpath(Element parent, String xpath) /*-{
-    return parent.ownerDocument.evaluate(
-        xpath, parent, null, XPathResult.ANY_TYPE, null).iterateNext();
-  }-*/; 
-  
+
   private static void ensureHiddenDiv() {
     // If the hidden DIV has not been created, create it.
     if (hiddenDiv == null) {
-      hiddenDiv = Document.get().createDivElement();      
+      hiddenDiv = Document.get().createDivElement();
+      UIObject.setVisible(hiddenDiv, false);
+      RootPanel.getBodyElement().appendChild(hiddenDiv);
     }
   }
 
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java b/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java
index 774e8c7..5fade12 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.XMLElement.Interpreter;
@@ -68,9 +67,7 @@
 
         HtmlInterpreter interpreter = HtmlInterpreter.newInterpreterForUiObject(
             writer, fieldName);
-        DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-        String innerHtml = child.consumeInnerHtml(interpreter, cursor);
-        writer.endDomSection();
+        String innerHtml = child.consumeInnerHtml(interpreter);
         if (innerHtml.length() > 0) {
           writer.addStatement("%s.%s().setHTML(\"%s\");", fieldName,
               faceNameGetter(faceName), innerHtml);
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java b/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java
index 36e2d54..00dfd26 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.user.client.ui.DialogBox;
@@ -41,9 +40,7 @@
 
         HtmlInterpreter interpreter = HtmlInterpreter.newInterpreterForUiObject(
             writer, fieldName);
-        DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-        caption = child.consumeInnerHtml(interpreter, cursor);
-        writer.endDomSection();
+        caption = child.consumeInnerHtml(interpreter);
       } else {
         if (body != null) {
           writer.die("In %s, may have only one widget, but found %s and %s",
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java b/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java
index 0bdc543..95d9745 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 
@@ -36,12 +35,10 @@
 
     interpreter.interpretElement(elem);
 
-    DomCursor cursor = writer.beginDomSection(fieldName);
-    
-    String html = elem.consumeOpeningTag() + elem.consumeInnerHtml(interpreter,
-        cursor)
+    writer.beginAttachedSection(fieldName);
+    String html = elem.consumeOpeningTag() + elem.consumeInnerHtml(interpreter)
         + elem.getClosingTag();
-    writer.endDomSection();
+    writer.endAttachedSection();
     writer.setFieldInitializer(fieldName, String.format(
         "(%1$s) UiBinderUtil.fromHtml(\"%2$s\")",
         type.getQualifiedSourceName(), html));
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
index 71a6d18..8c2f3c1 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
@@ -37,8 +37,17 @@
       throws UnableToCompleteException {
     String fieldName = writer.declareFieldIfNeeded(elem);
     if (fieldName != null) {
-      writer.declareDomField(fieldName, elem.getLocalName());
-    }    
+      String token = writer.declareDomField(fieldName, element);
+
+      if (elem.hasAttribute("id")) {
+        writer.die(String.format(
+            "Cannot declare id=\"%s\" and %s=\"%s\" on the same element",
+            elem.consumeRawAttribute("id"), writer.getUiFieldAttributeName(),
+            fieldName));
+      }
+
+      elem.setAttribute("id", token);
+    }
 
     /*
      * Return null because we don't want to replace the dom element with any
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
index e8d0991..83d65f7 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
@@ -18,7 +18,6 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.uibinder.elementparsers.HtmlMessageInterpreter.PlaceholderInterpreterProvider;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.messages.MessageWriter;
@@ -46,10 +45,10 @@
      */
     HtmlInterpreter htmlInterpreter = makeHtmlInterpreter(fieldName, writer);
 
-    DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
+    writer.beginAttachedSection(fieldName + ".getElement()");
     String html = elem.consumeInnerHtml(InterpreterPipe.newPipe(
-        widgetInterpreter, htmlInterpreter), cursor);
-    writer.endDomSection();
+        widgetInterpreter, htmlInterpreter));
+    writer.endAttachedSection();
 
     /*
      * HTMLPanel has no no-arg ctor, so we have to generate our own, using the
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java b/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java
index 80cdda9..601c03d 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 
@@ -29,12 +28,11 @@
   public void parse(XMLElement elem, String fieldName, JClassType type,
       UiBinderWriter writer) throws UnableToCompleteException {
 
-    writer.addInitComment("HasHtmlParser.parse");
     HtmlInterpreter interpreter =
       HtmlInterpreter.newInterpreterForUiObject(writer, fieldName);
-    DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-    String html = elem.consumeInnerHtml(interpreter, cursor);
-    writer.endDomSection();
+    writer.beginAttachedSection(fieldName + ".getElement()");
+    String html = elem.consumeInnerHtml(interpreter);
+    writer.endAttachedSection();
     // TODO(jgw): throw an error if there's a conflicting 'html' attribute.
     if (html.trim().length() > 0) {
       writer.genStringPropertySet(fieldName, "HTML", html);
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
index 24555fb..f21ac6d 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
@@ -73,8 +73,7 @@
       }
 
       MessageWriter message = messages.newMessage(elem);
-      message.setDefaultMessage(elem.consumeInnerHtml(phiProvider.get(message),
-          uiWriter.getCurrentDomCursor()));
+      message.setDefaultMessage(elem.consumeInnerHtml(phiProvider.get(message)));
       return uiWriter.tokenForExpression(messages.declareMessage(message));
     }
 
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlPlaceholderInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlPlaceholderInterpreter.java
index 3966680..0819505 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlPlaceholderInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlPlaceholderInterpreter.java
@@ -16,7 +16,6 @@
 package com.google.gwt.uibinder.elementparsers;
 
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.messages.MessageWriter;
@@ -65,15 +64,12 @@
        * This recursive innerHtml call has already been escaped. Hide it in a
        * token to avoid double escaping
        */
-      DomCursor currentDomCursor = uiWriter.getCurrentDomCursor();
-      String body = tokenator.nextToken(elem.consumeInnerHtml(this, 
-          currentDomCursor));      
-      
+      String body = tokenator.nextToken(elem.consumeInnerHtml(this));
+
       String closeTag = elem.getClosingTag();
       String closePlaceholder =
           nextPlaceholder(name + "End", closeTag, closeTag);
 
-      currentDomCursor.advanceChild();
       return openPlaceholder + body + closePlaceholder;
     }
 
@@ -83,8 +79,7 @@
   @Override
   protected String consumePlaceholderInnards(XMLElement elem)
       throws UnableToCompleteException {
-    return elem.consumeInnerHtml(fieldAndComputed, 
-        uiWriter.getCurrentDomCursor());
+    return elem.consumeInnerHtml(fieldAndComputed);
   }
 
   /**
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java
index 43384ad..7feb8ad 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java
@@ -19,7 +19,6 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.user.client.ui.StackLayoutPanel;
@@ -76,9 +75,7 @@
         HtmlInterpreter htmlInt = HtmlInterpreter.newInterpreterForUiObject(
             writer, fieldName);
         String size = children.header.consumeRequiredDoubleAttribute("size");
-        DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-        String html = children.header.consumeInnerHtml(htmlInt, cursor);
-        writer.endDomSection();
+        String html = children.header.consumeInnerHtml(htmlInt);
         writer.addStatement("%s.add(%s, \"%s\", true, %s);", fieldName,
             childFieldName, html, size);
       } else if (children.customHeader != null) {
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java
index cb3aceb..a6880a9 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java
@@ -19,7 +19,6 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.user.client.ui.TabLayoutPanel;
@@ -81,9 +80,7 @@
       if (children.header != null) {
         HtmlInterpreter htmlInt = HtmlInterpreter.newInterpreterForUiObject(
             writer, fieldName);
-        DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-        String html = children.header.consumeInnerHtml(htmlInt, cursor);
-        writer.endDomSection();
+        String html = children.header.consumeInnerHtml(htmlInt);
         writer.addStatement("%s.add(%s, \"%s\", true);", fieldName,
             childFieldName, html);
       } else if (children.customHeader != null) {
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java
index 45902b8..627512b 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 
@@ -60,9 +59,7 @@
         if (tabChild.getLocalName().equals(TAG_TABHTML)) {
           HtmlInterpreter interpreter = HtmlInterpreter.newInterpreterForUiObject(
               writer, fieldName);
-          DomCursor cursor = writer.beginDomSection(fieldName + ".getElement()");
-          tabHTML = tabChild.consumeInnerHtml(interpreter, cursor);
-          writer.endDomSection();
+          tabHTML = tabChild.consumeInnerHtml(interpreter);
         } else {
           if (childFieldName != null) {
             writer.die("%s may only have a single child widget", child);
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
index 6edc8e4..686cb2a 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
@@ -68,26 +68,30 @@
 
   public String interpretElement(XMLElement elem) 
       throws UnableToCompleteException {
-    if (uiWriter.isWidgetElement(elem)) { 
-      
-      String tag = getLegalPlaceholderTag(elem);
-      
+    if (uiWriter.isWidgetElement(elem)) {
+      // Allocate a local variable to hold the dom id for this widget. Note
+      // that idHolder is a local variable reference, not a string id. We
+      // have to generate the ids at runtime, not compile time, or else
+      // we'll reuse ids for any template rendered more than once.
+      String idHolder = uiWriter.declareDomIdHolder();
       String childField = uiWriter.parseElementToField(elem);
+      uiWriter.ensureFieldAttached(fieldName);
       
-      String elementPointer = "element" + uiWriter.getUniqueId();
+      String elementPointer = idHolder + "Element";
       uiWriter.addInitStatement(
-          "com.google.gwt.user.client.Element %s = %s;",
-          elementPointer, uiWriter.getDomAccessExpression(elementPointer, tag));
-
-      uiWriter.addInitStatement(
+          "com.google.gwt.user.client.Element %s = " +
+          "com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
+          elementPointer, idHolder);
+      // Delay replacing the placeholders with the widgets until after 
+      // detachment so as not to end up attaching the widget to the DOM 
+      // unnecessarily
+      uiWriter.addDetachStatement(
           "%1$s.addAndReplaceElement(%2$s, %3$s);", 
           fieldName, childField, elementPointer);      
 
-      
-      // Increment DOM cursor based on the tag we are adding.
-      uiWriter.getCurrentDomCursor().advanceChild();
       // Create an element to hold the widget.
-      return "<" + tag + "></" + tag + ">";
+      String tag = getLegalPlaceholderTag(elem);
+      return "<" + tag + " id='\" + " + idHolder + " + \"'></" + tag + ">";
     }
     return null;
   }
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
index 23a65a1..4080258 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
@@ -18,7 +18,6 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.uibinder.rebind.DomCursor;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.messages.MessageWriter;
@@ -62,10 +61,10 @@
   private int serial = 0;
   private final String ancestorExpression;
   private final String fieldName;
-  private final Map<String, XMLElement> elementHolderToWidgetElement = 
-      new HashMap<String, XMLElement>();
-  private final Set<String> elementIsHasHTML = new HashSet<String>();
-  private final Set<String> elementIsHasText = new HashSet<String>();
+  private final Map<String, XMLElement> idToWidgetElement =
+    new HashMap<String, XMLElement>();
+  private final Set<String> idIsHasHTML = new HashSet<String>();
+  private final Set<String> idIsHasText = new HashSet<String>();
 
   WidgetPlaceholderInterpreter(String fieldName, UiBinderWriter writer,
       MessageWriter message, String ancestorExpression) {
@@ -82,7 +81,6 @@
       return super.interpretElement(elem);
     }
 
-    uiWriter.addInitComment("WidgetPlaceholderInterpreter.interpretElement");
     JClassType type = uiWriter.findFieldType(elem);
     TypeOracle oracle = uiWriter.getOracle();
 
@@ -92,46 +90,39 @@
       name = "widget" + (++serial);
     }
 
-    DomCursor cursor = uiWriter.getCurrentDomCursor();
-    String elementHolder = cursor.getAccessExpression();
-    // We are going to generate another element right here, so we advance the child pointer.
-    cursor.advanceChild();
-    elementHolderToWidgetElement.put(elementHolder, elem);
+    String idHolder = uiWriter.declareDomIdHolder();
+    idToWidgetElement.put(idHolder, elem);
 
     if (oracle.findType(HasHTML.class.getName()).isAssignableFrom(type)) {
-      return handleHasHTMLPlaceholder(elem, name, elementHolder);
+      return handleHasHTMLPlaceholder(elem, name, idHolder);
     }
 
     if (oracle.findType(HasText.class.getName()).isAssignableFrom(type)) {
-      return handleHasTextPlaceholder(elem, name, elementHolder);
+      return handleHasTextPlaceholder(elem, name, idHolder);
    }
 
-    return handleOpaqueWidgetPlaceholder(name);
+    return handleOpaqueWidgetPlaceholder(name, idHolder);
   }
 
- 
   /**
    * Called by {@link XMLElement#consumeInnerHtml} after all elements
    * have been handed to {@link #interpretElement}.
    */
   @Override
   public String postProcess(String consumed) throws UnableToCompleteException {
-    for (Map.Entry<String, XMLElement> entry : elementHolderToWidgetElement.entrySet()) {
-      String element = entry.getKey();
-      XMLElement childElem = entry.getValue();
+    for (String idHolder : idToWidgetElement.keySet()) {
+      XMLElement childElem = idToWidgetElement.get(idHolder);
       String childField = uiWriter.parseElementToField(childElem);
 
-      genSetWidgetTextCall(element, childField);
-      
-      uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, " +
-          "(com.google.gwt.user.client.Element)%3$s);",
-          fieldName, childField, element);
+      genSetWidgetTextCall(idHolder, childField);
+      uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, %3$s);",
+          fieldName, childField, idHolder);
     }
     /*
      * We get used recursively, so this will be called again. Empty the map 
      * or else we'll re-register things.
      */
-    elementHolderToWidgetElement.clear();
+    idToWidgetElement.clear();
     return super.postProcess(consumed);
   }
 
@@ -141,36 +132,34 @@
     return closePlaceholder;
   }
 
-  private String genOpenTag(String name) {
-    String openTag = "<span>";
+  private String genOpenTag(String name, String idHolder) {
+    String openTag = String.format("<span id='\" + %s + \"'>", idHolder);
     String openPlaceholder =
         nextPlaceholder(name + "Begin", "<span>", openTag);
     return openPlaceholder;
   }
 
-  private void genSetWidgetTextCall(String elementHolder, String childField) {
-    if (elementIsHasText.contains(elementHolder)) {
+  private void genSetWidgetTextCall(String idHolder, String childField) {
+    if (idIsHasText.contains(idHolder)) {
       uiWriter.addInitStatement(
-          "%s.setText(((com.google.gwt.user.client.Element)%s).getInnerText());", 
-          childField, elementHolder);
+          "%s.setText(%s.getElementById(%s).getInnerText());", childField,
+          fieldName, idHolder);
     }
-    if (elementIsHasHTML.contains(elementHolder)) {
+    if (idIsHasHTML.contains(idHolder)) {
       uiWriter.addInitStatement(
-          "%s.setHTML(((com.google.gwt.user.client.Element)%s).getInnerHTML());", 
-          childField, elementHolder);
+          "%s.setHTML(%s.getElementById(%s).getInnerHTML());", childField,
+          fieldName, idHolder);
     }
   }
 
   private String handleHasHTMLPlaceholder(XMLElement elem, String name,
-      String elementHolder) throws UnableToCompleteException {
-    elementIsHasHTML.add(elementHolder);
-    String openPlaceholder = genOpenTag(name);
+      String idHolder) throws UnableToCompleteException {
+    idIsHasHTML.add(idHolder);
+    String openPlaceholder = genOpenTag(name, idHolder);
 
-    DomCursor cursor = uiWriter.beginDomSection(elementHolder);
     String body =
         elem.consumeInnerHtml(new HtmlPlaceholderInterpreter(uiWriter,
-            message, ancestorExpression), cursor);
-    uiWriter.endDomSection();
+            message, ancestorExpression));
     String bodyToken = tokenator.nextToken(body);
 
     String closePlaceholder = genCloseTag(name);
@@ -178,9 +167,9 @@
   }
 
   private String handleHasTextPlaceholder(XMLElement elem, String name,
-      String elementHolder) throws UnableToCompleteException {
-    elementIsHasText.add(elementHolder);
-    String openPlaceholder = genOpenTag(name);
+      String idHolder) throws UnableToCompleteException {
+    idIsHasText.add(idHolder);
+    String openPlaceholder = genOpenTag(name, idHolder);
 
     String body =
         elem.consumeInnerText(new TextPlaceholderInterpreter(uiWriter,
@@ -191,8 +180,8 @@
     return openPlaceholder + bodyToken + closePlaceholder;
   }
 
-  private String handleOpaqueWidgetPlaceholder(String name) {
-    String tag = "<span></span>";
+  private String handleOpaqueWidgetPlaceholder(String name, String idHolder) {
+    String tag = String.format("<span id='\" + %s + \"'></span>", idHolder);
     String placeholder = nextPlaceholder(name, "<span></span>", tag);
     return placeholder;
   }
diff --git a/user/src/com/google/gwt/uibinder/rebind/DomCursor.java b/user/src/com/google/gwt/uibinder/rebind/DomCursor.java
deleted file mode 100644
index 76e3f00..0000000
--- a/user/src/com/google/gwt/uibinder/rebind/DomCursor.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.uibinder.rebind;
-
-import com.google.gwt.core.ext.UnableToCompleteException;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * DOM Cursor keeps track of a given path in the DOM.  This is useful for
- * plucking out nodes from a DOM tree.
- */
-public class DomCursor {
-  
-  private static class PathComponent {    
-    
-    private int childIndex;
-    private boolean isTableWithoutTbody;
-    
-    public PathComponent(XMLElement element) {
-      this.childIndex = 0;
-      this.isTableWithoutTbody = "table".equalsIgnoreCase(element.getLocalName());
-    }
-    
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) return true;
-      if (obj == null) return false;
-      if (!(obj instanceof PathComponent)) return false;
-      PathComponent other = (PathComponent) obj;      
-      if (childIndex != other.childIndex) return false;
-      if (isTableWithoutTbody != other.isTableWithoutTbody) return false;
-      return true;
-    }
-    
-    @Override
-    public int hashCode() {
-      final int prime = 31;
-      int result = 1;      
-      result = prime * result + childIndex;
-      result = prime * result + (isTableWithoutTbody ? 1231 : 1237);
-      return result;
-    }
-    
-    public void incrementIndex() {
-      childIndex++;
-    }    
-  }
-  
-  private class ParagraphTracking {
-    private LinkedList<Boolean> paragraphUnSafeNodes = new LinkedList<Boolean>();
-    
-    public void beginUnsafeTag() {
-      paragraphUnSafeNodes.addLast(true);
-    }
-    
-    public void endUnsafeTag() {
-      paragraphUnSafeNodes.removeLast();
-    }
-    
-    public boolean isSafeForField() {
-      return paragraphUnSafeNodes.isEmpty();
-    }
-  }
-  
-  // Tags that cause P tags to end weirdly violating XHTML 
-  // http://dev.w3.org/html5/spec/Overview.html#parsing-main-inbody
-  private static final Set<String> PARAGRAPH_UNSAFE_NODES = new HashSet<String>(Arrays.asList(
-    new String[] {"address", "article", "aside", "blockquote", "center", "details", "dir", "div", 
-    "dl", "fieldset", "figure", "footer", "header", "hgroup", "menu", "nav", "ol", "p", "section",
-    "ul"}));
-  private static final Set<String> TABLE_SECTIONS = new HashSet<String>(Arrays.asList(new String[] {
-    "thead", "tbody", "tfoot"}));
-  private static final String NON_TEXT_LOOKUP_METHOD = "UiBinderUtil.getNonTextChild";
-  private static final String STANDARD_LOOKUP_METHOD = "UiBinderUtil.getChild";
-  private static final String TABLE_NON_TEXT_LOOKUP_METHOD = "UiBinderUtil.getTableChild";
-
-  // Cache of path to a local variable that contains that.
-  private Map<LinkedList<PathComponent>, String> domPathCache = 
-    new HashMap<LinkedList<PathComponent>, String>();
-  private LinkedList<ParagraphTracking> paragraphs = new LinkedList<ParagraphTracking>();
-  private final String parent;
-  private LinkedList<PathComponent> pathComponents = new LinkedList<PathComponent>();
-  private boolean preserveWhitespaceNodes = true;
-  private boolean walkingTextChildNodes = false;
-  private final UiBinderWriter writer;
-  
-  public DomCursor(String parent, UiBinderWriter writer) {
-    this.parent = parent;   
-    this.writer = writer;
-  }
-  
-  public void advanceChild() {
-    pathComponents.getLast().incrementIndex();
-    writer.addInitComment("advance DomCursor %s", this.toString());
-  }
-  
-  public void advanceChildForWhitespaceText() {
-    if (preserveWhitespaceNodes) {
-      advanceTextChild();
-    }
-  }
-  
-  public void advanceTextChild() {
-    if (walkingTextChildNodes) {
-      advanceChild();
-    }
-  }
-  
-  /**
-   * Finish visiting this subtree of the DOM.
-   */
-  public void finishChild(XMLElement elem) {
-    if (!writer.getMessages().isMessage(elem) &&
-        !isPlaceholderElement(elem)) {
-      pathComponents.removeLast();
-      writer.addInitComment("finish DomCursor %s", this.toString());
-      String tag = elem.getLocalName();
-      if ("p".equalsIgnoreCase(tag)) {
-        endParagraph();
-      }
-      if (isInsideParagraph() && isUnsafeParagraphTag(tag)) {
-        paragraphs.getLast().endUnsafeTag();        
-      }      
-    }
-  }
-  
-
-  public String getAccessExpression() throws UnableToCompleteException {
-    return getAccessExpression(null, null);
-  }
-  
-  /**
-   * Returns a Java expression for referencing the given node.
-   * 
-   * @param localVar Optional variable that will be used to cache the result of the expression.
-   *        If there is no applicable local variable, callers can pass null.
-   *
-   * @param tag Optional tag name of the tag that would be referenced.  This can be null.
-   * @return Java access expression.
-   * @throws UnableToCompleteException 
-   */
-  public String getAccessExpression(String localVar, String tag) 
-      throws UnableToCompleteException {
-    // P tags cause all sorts of problems with hierarchy as the HTML spec has lots of weird
-    // semantics for block elements inside of P tags.
-    if (!safeForExpression()) {
-      writer.die("UiBinder no longer allows certain addressable  " +
-            "elements inside of <p> tags because of browser " +
-            "inconsistency, consider using DIV instead");
-    }
-    
-    String result = getDomWalkAccessExpression(tag);
-    if (localVar != null) {
-      domPathCache.put(new LinkedList<PathComponent>(pathComponents), localVar);
-      return result;
-    }
-    
-    String varName = "intermediate" + writer.getUniqueId();
-    writer.addInitStatement("com.google.gwt.dom.client.Node %s = %s;", varName, result); 
-    domPathCache.put(new LinkedList<PathComponent>(pathComponents), varName);
-    return varName;
-  }
-  
-  /**
-   * Visit a child subtree of the DOM.
-   * @throws UnableToCompleteException 
-   */
-  public void visitChild(XMLElement elem) throws UnableToCompleteException {
-    if (!writer.getMessages().isMessage(elem) &&
-        !isPlaceholderElement(elem)) {
-      // If we do see an actual tbody in the uibinder, we should stop trying to account for the
-      // automatic one that the browser will insert.
-      String tag = elem.getLocalName();
-      if ("tbody".equalsIgnoreCase(tag)) {
-        pathComponents.getLast().isTableWithoutTbody = false;
-      }
-      if ("td".equalsIgnoreCase(tag) && !"tr".equalsIgnoreCase(elem.getParent().getLocalName())) {
-        writer.die("TD tags must be inside of a TR tag");
-      }
-      pathComponents.addLast(new PathComponent(elem));
-      writer.addInitComment("visit DomCursor %s", this.toString());
-      
-      if (isInsideParagraph() && isUnsafeParagraphTag(tag)) {
-        paragraphs.getLast().beginUnsafeTag();        
-      }
-      if ("p".equalsIgnoreCase(tag)) {
-        beginParagraph();
-      }
-    }    
-  }
-  
-  private void beginParagraph() {
-    paragraphs.addLast(new ParagraphTracking());
-  }
-  
-  private void endParagraph() {
-    paragraphs.removeLast();
-  }
-  
-  private String getDomWalkAccessExpression(String tag) {  
-    writer.addInitComment("DomWalkAccess %s", this.toString());
-    
-    // First look and see if we have any part of the path in our variable cache.
-    int end = pathComponents.size();
-    String var = null;
-    for (; end > 0; --end) {
-      var = domPathCache.get(pathComponents.subList(0, end));
-      if (var != null) {
-        break;
-      }
-    }
-    
-    // Next, do the remaining DOM walking
-    StringBuilder builder = new StringBuilder();
-    if (var != null) {
-      builder.append(var);
-    } else {
-      builder.append(parent);
-    }
-    for (int i = end; i < pathComponents.size(); ++i) {
-      PathComponent component = pathComponents.get(i);
-      if (i < (pathComponents.size() - 1)) {
-        // For partial paths, create an intermediate variable that can be reused
-        // by other elements that need to walk.
-        String varName = "intermediate" + writer.getUniqueId();
-        writer.addInitStatement("com.google.gwt.dom.client.Node %s = %s(%s, %d);", 
-            varName, getLookupMethod(component, tag), builder.toString(), 
-            component.childIndex);
-        domPathCache.put(new LinkedList<PathComponent>(pathComponents.subList(0, i + 1)), varName);
-        builder = new StringBuilder(varName);
-      } else {
-        builder.insert(0, getLookupMethod(component, tag) + "(");
-        builder.append(", ").append(component.childIndex).append(")");        
-      }
-    }
-    builder.append(".cast()");
-    return builder.toString();
-  }
-  
-  private String getLookupMethod(PathComponent component, String tag) {
-    if (walkingTextChildNodes) {
-      return STANDARD_LOOKUP_METHOD;
-    }
-    if (component.isTableWithoutTbody && !isValidDirectTableChild(tag)) {
-      return TABLE_NON_TEXT_LOOKUP_METHOD;
-    }
-    return NON_TEXT_LOOKUP_METHOD;
-  }
-  
-  private String getQuery() {
-    StringBuilder query = new StringBuilder();
-
-    for (PathComponent component : pathComponents) {
-      if (query.length() > 0) {
-        query.append(" > ");
-      }
-      query.append(":nth-child(").append(component.childIndex + 1).append(")");
-    }
-    return query.toString();
-  }
-  
-  /**
-   * Get an access expression using XPATH or CSS query.  This is currently disabled as it isn't
-   * as fast as walking by hand.
-   * @return
-   */
-  private String getQueryAccessExpression() {  
-    StringBuilder builder = new StringBuilder("UiBinderUtil.lookupNodeByTreeIndicies(");
-    builder.append(parent);
-    builder.append(",\"");
-    builder.append(getQuery());
-    builder.append("\",\"");
-    builder.append(getXpath());
-    builder.append("\").cast()");
-    return builder.toString();
-  }
-  
-  private String getXpath() {
-    StringBuilder xpath = new StringBuilder();
-    for (PathComponent component : pathComponents) {        
-      xpath.append("/*[").append(component.childIndex + 1).append("]");
-    }
-    return xpath.toString();
-  }
-  
-  private boolean isInsideParagraph() {
-    return !paragraphs.isEmpty();
-  }
-
-  private boolean isPlaceholderElement(XMLElement elem) {
-    return "ph".equalsIgnoreCase(elem.getLocalName());
-  }
-  
-  private boolean isUnsafeParagraphTag(String tag) {
-    return PARAGRAPH_UNSAFE_NODES.contains(tag.toLowerCase());
-  }
-  
-  private boolean isValidDirectTableChild(String tag) {
-    return tag != null && TABLE_SECTIONS.contains(tag.toLowerCase());
-  }
-  
-  private boolean safeForExpression() {
-    for (ParagraphTracking tracking : paragraphs) {
-      if (!tracking.isSafeForField()) {
-        return false;
-      }
-    }
-    return true;
-  }
-}
diff --git a/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java b/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java
index 5fc6d17..4eae5dd 100644
--- a/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java
+++ b/user/src/com/google/gwt/uibinder/rebind/GetInnerHtmlVisitor.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -19,62 +19,38 @@
 import com.google.gwt.uibinder.rebind.XMLElement.Interpreter;
 
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.Text;
 
 class GetInnerHtmlVisitor extends GetEscapedInnerTextVisitor {
 
   /**
    * Recursively gathers an HTML representation of the children of the given
-   * Element, and stuffs it into the given StringBuffer. Applies the interpreter to
+   * Elem, and stuffs it into the given StringBuffer. Applies the interpreter to
    * each descendant, and uses the writer to report errors.
    */
   public static void getEscapedInnerHtml(Element elem, StringBuffer buffer,
-      Interpreter<String> interpreter, XMLElementProvider writer, DomCursor domCursor, 
-      MortalLogger logger)
-      throws UnableToCompleteException {   
-    XMLElement xmlElement = writer.get(elem);
-    domCursor.visitChild(xmlElement);
-    new ChildWalker().accept(elem, new GetInnerHtmlVisitor(buffer, interpreter, writer, domCursor,
-        logger));
-    domCursor.finishChild(xmlElement);
+      Interpreter<String> interpreter, XMLElementProvider writer)
+      throws UnableToCompleteException {
+    new ChildWalker().accept(elem, new GetInnerHtmlVisitor(buffer, interpreter,
+        writer));
   }
-  
-  private DomCursor domCursor;
-  private final MortalLogger logger;
 
-  private GetInnerHtmlVisitor(StringBuffer buffer, Interpreter<String> interpreter,
-      XMLElementProvider writer, DomCursor domCursor, MortalLogger logger) {
+  private GetInnerHtmlVisitor(StringBuffer buffer,
+      Interpreter<String> interpreter, XMLElementProvider writer) {
     super(buffer, interpreter, writer);
-    this.domCursor = domCursor;
-    this.logger = logger;
   }
-  
+
   @Override
   public void visitElement(Element elem) throws UnableToCompleteException {
     XMLElement xmlElement = elementProvider.get(elem);
     String replacement = interpreter.interpretElement(xmlElement);
-
     if (replacement != null) {
       buffer.append(replacement);
       return;
     }
 
-    Node parent = elem.getParentNode();        
-    buffer.append(xmlElement.consumeOpeningTag());    
-    getEscapedInnerHtml(elem, buffer, interpreter, elementProvider, domCursor, logger);
-    buffer.append(xmlElement.getClosingTag());       
-    domCursor.advanceChild();
-  }
-
-  @Override
-  public void visitText(Text t) {
-    int startLength = buffer.length();
-    super.visitText(t);
-    if (buffer.length() != startLength && !buffer.toString().matches("^\\s*$")) {
-      if (buffer.toString().substring(startLength).matches("^\\s*$")) {
-        domCursor.advanceTextChild();
-      }
-    }
+    // TODO(jgw): Ditch the closing tag when there are no children.
+    buffer.append(xmlElement.consumeOpeningTag());
+    getEscapedInnerHtml(elem, buffer, interpreter, elementProvider);
+    buffer.append(xmlElement.getClosingTag());
   }
 }
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index a7ba90a..a013d27 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -105,7 +105,7 @@
     text = text.replaceAll(">", "&gt;");
 
     if (!preserveWhitespace) {
-      text = text.replaceAll("\\s+", " ");      
+      text = text.replaceAll("\\s+", " ");
     }
 
     return escapeTextForJavaStringLiteral(text);
@@ -201,13 +201,22 @@
   private String gwtPrefix;
 
   private String rendered;
-  
+
   /**
    * Stack of element variable names that have been attached.
    */
-  private final LinkedList<DomCursor> domSectionElements = 
-      new LinkedList<DomCursor>();
-  
+  private final LinkedList<String> attachSectionElements = new LinkedList<String>();
+  /**
+   * Maps from field element name to the temporary attach record variable name.
+   */
+  private final Map<String, String> attachedVars = new HashMap<String, String>();
+  private int nextAttachVar = 0;
+
+  /**
+   * Stack of statements to be executed after we detach the current attach
+   * section.
+   */
+  private final LinkedList<List<String>> detachStatementsStack = new LinkedList<List<String>>();
   private final AttributeParsers attributeParsers;
   private final BundleAttributeParsers bundleParsers;
 
@@ -258,13 +267,18 @@
   }
 
   /**
-   * Adds a comment in the current init stream.  Useful for debugging generated code.
+   * Add a statement to be executed right after the current attached element is
+   * detached. This is useful for doing things that might be expensive while the
+   * element is attached to the DOM.
+   * 
    * @param format
-   * @param params
+   * @param args
+   * @see #beginAttachedSection(String)
    */
-  public void addInitComment(String format, Object... params) {
-    addInitStatement("// " + format, params);
+  public void addDetachStatement(String format, Object... args) {
+    detachStatementsStack.getFirst().add(String.format(format, args));
   }
+
   /**
    * Add a statement to be run after everything has been instantiated, in the
    * style of {@link String#format}.
@@ -282,35 +296,50 @@
   }
 
   /**
-   * Begin a section where a new DOM tree is being parsed--that is,
-   * one that will be constructed as a big innerHTML string, and then walked to
-   * allow fields accessing its to be filled (at the
+   * Begin a section where a new attachable element is being parsed--that is,
+   * one that will be constructed as a big innerHTML string, and then briefly
+   * attached to the dom to allow fields accessing its to be filled (at the
    * moment, HasHTMLParser, HTMLPanelParser, and DomElementParser.).
    * <p>
+   * Succeeding calls made to {@link #ensureAttached} and
+   * {@link #ensureFieldAttached} must refer to children of this element, until
+   * {@link #endAttachedSection} is called.
    * 
    * @param element Java expression for the generated code that will return the
-   *          DOM element to be attached.
+   *          dom element to be attached.
    */
-  public DomCursor beginDomSection(String element) {
-    DomCursor cursor = new DomCursor(element, this);
-    domSectionElements.addFirst(cursor);
-    addInitComment("writer.beginAttachedSection %s", cursor.toString());
-    return cursor;
+  public void beginAttachedSection(String element) {
+    attachSectionElements.addFirst(element);
+    detachStatementsStack.addFirst(new ArrayList<String>());
   }
 
   /**
-   * Declare a field that will hold an Element instance. 
+   * Declare a field that will hold an Element instance. Returns a token that
+   * the caller must set as the id attribute of that element in whatever
+   * innerHTML expression will reproduce it at runtime.
+   * <P>
+   * In the generated code, this token will be replaced by an expression to
+   * generate a unique dom id at runtime. Further code will be generated to be
+   * run after widgets are instantiated, to use that dom id in a getElementById
+   * call and assign the Element instance to its field.
    * 
    * @param fieldName The name of the field being declared
-   * @param tag The tag name of the DOM element that is being referenced.
-   * @throws UnableToCompleteException 
+   * @param parentElementExpression an expression to be evaluated at runtime,
+   *          which will return an Element that is an ancestor of this one
+   *          (needed by the getElementById call mentioned above).
    */
-  public void declareDomField(String fieldName, String tag) throws UnableToCompleteException {    
+  public String declareDomField(String fieldName, String parentElementExpression)
+      throws UnableToCompleteException {
+    ensureAttached(parentElementExpression);
+    String name = declareDomIdHolder();
     setFieldInitializer(fieldName, "null");
-    addInitStatement("%s = %s;", fieldName, 
-        domSectionElements.getFirst().getAccessExpression(fieldName, tag));
+    addInitStatement(
+        "%s = com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
+        fieldName, name);
+    addInitStatement("%s.removeAttribute(\"id\");", fieldName);
+    return tokenForExpression(name);
   }
-  
+
   /**
    * Declare a variable that will be filled at runtime with a unique id, safe
    * for use as a dom element's id attribute.
@@ -318,15 +347,13 @@
    * @return that variable's name.
    */
   public String declareDomIdHolder() throws UnableToCompleteException {
-    String domHolderName = "domId" + getUniqueId();
+    String domHolderName = "domId" + domId++;
     FieldWriter domField = fieldManager.registerField(
         oracle.findType(String.class.getName()), domHolderName);
     domField.setInitializer("com.google.gwt.dom.client.Document.get().createUniqueId()");
     return domHolderName;
   }
 
-
-  
   /**
    * Declares a field of the given type name, returning the name of the declared
    * field. If the element has a field or id attribute, use its value.
@@ -400,11 +427,48 @@
    * End the current attachable section. This will detach the element if it was
    * ever attached and execute any detach statements.
    * 
-   * @see #beginDomSection(String)
+   * @see #beginAttachedSection(String)
    */
-  public void endDomSection() {
-    DomCursor cursor = domSectionElements.removeFirst();
-    addInitComment("writer.endAttachedSection %s", cursor.toString());
+  public void endAttachedSection() {
+    String elementVar = attachSectionElements.removeFirst();
+    List<String> detachStatements = detachStatementsStack.removeFirst();
+    if (attachedVars.containsKey(elementVar)) {
+      String attachedVar = attachedVars.remove(elementVar);
+      addInitStatement("%s.detach();", attachedVar);
+      for (String statement : detachStatements) {
+        addInitStatement(statement);
+      }
+    }
+  }
+
+  /**
+   * Ensure that the specified element is attached to the DOM.
+   * 
+   * @param element variable name of element to be attached
+   * @see #beginAttachedSection(String)
+   */
+  public void ensureAttached(String element) {
+    String attachSectionElement = attachSectionElements.getFirst();
+    if (!attachedVars.containsKey(attachSectionElement)) {
+      String attachedVar = "attachRecord" + nextAttachVar;
+      addInitStatement(
+          "UiBinderUtil.TempAttachment %s = UiBinderUtil.attachToDom(%s);",
+          attachedVar, attachSectionElement);
+      attachedVars.put(attachSectionElement, attachedVar);
+      nextAttachVar++;
+    }
+  }
+
+  /**
+   * Ensure that the specified field is attached to the DOM. The field must hold
+   * an object that responds to Element getElement(). Convenience wrapper for
+   * {@link ensureAttached}<code>(field + ".getElement()")</code>.
+   * 
+   * @param field variable name of the field to be attached
+   * @see #beginAttachedSection(String)
+   */
+  public void ensureFieldAttached(String field) {
+    ensureAttached(field + ".getElement()");
   }
 
   /**
@@ -472,35 +536,14 @@
   public ImplicitClientBundle getBundleClass() {
     return bundleClass;
   }
-  
-  /**
-   * Get the current cursor for the DOM.  This can be used for accessing expressions, or walking
-   * subtrees.
-   */
-  public DomCursor getCurrentDomCursor() {
-    return domSectionElements.getFirst();
-  }
 
   /**
-   * Returns an expression for accessing the current position in the DOM.
-   * @param varName optional variable where the result of this expression will be stored for 
-   *        cacheing
-   * @param tag optional name of the HTML tag we are using the expression for.
-   * @throws UnableToCompleteException 
-   */
-  public String getDomAccessExpression(String varName, String tag) 
-      throws UnableToCompleteException {
-    addInitComment("getDomAccessExpression %s", domSectionElements.getFirst().toString());
-    return domSectionElements.getFirst().getAccessExpression(varName, tag);
-  }
-  
-  /**
    * @return The logger, at least until we get get it handed off to parsers via
    *         constructor args.
    */
   public MortalLogger getLogger() {
     return logger;
-  }  
+  }
 
   /**
    * Get the {@link MessagesWriter} for this UI, generating it if necessary.
@@ -524,10 +567,6 @@
     return gwtPrefix + ":field";
   }
 
-  public int getUniqueId() {
-    return domId++;
-  }
-  
   public boolean isBinderElement(XMLElement elem) {
     String uri = elem.getNamespaceUri();
     return uri != null && UiBinderGenerator.BINDER_URI.equals(uri);
@@ -684,11 +723,15 @@
    * 
    * @throws UnableToCompleteException
    */
-  private void ensureCursorCleanedUp() {
-    if (!domSectionElements.isEmpty()) {
+  private void ensureAttachmentCleanedUp() throws UnableToCompleteException {
+    if (!attachSectionElements.isEmpty()) {
       throw new IllegalStateException("Attachments not cleaned up: "
-          + domSectionElements);
-    }    
+          + attachSectionElements);
+    }
+    if (!detachStatementsStack.isEmpty()) {
+      throw new IllegalStateException("Detach not cleaned up: "
+          + detachStatementsStack);
+    }
   }
 
   /**
@@ -854,7 +897,7 @@
         new PrintWriter(stringWriter));
     writeBinder(niceWriter, rootField);
 
-    ensureCursorCleanedUp();
+    ensureAttachmentCleanedUp();
     return stringWriter.toString();
   }
 
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
index ae1c1db..b9b9377 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -379,15 +379,13 @@
    * @param interpreter Called for each element, expected to return a string
    *          replacement for it, or null if it should be left as is
    */
-  public String consumeInnerHtml(Interpreter<String> interpreter,
-      DomCursor domCursor)
+  public String consumeInnerHtml(Interpreter<String> interpreter)
       throws UnableToCompleteException {
     if (interpreter == null) {
       throw new NullPointerException("interpreter must not be null");
     }
     StringBuffer buf = new StringBuffer();
-    GetInnerHtmlVisitor.getEscapedInnerHtml(elem, buf, interpreter, provider,
-        domCursor, logger);
+    GetInnerHtmlVisitor.getEscapedInnerHtml(elem, buf, interpreter, provider);
 
     clearChildren(elem);
     return buf.toString().trim();
@@ -397,11 +395,9 @@
    * Refines {@link #consumeInnerHtml(Interpreter)} to handle
    * PostProcessingInterpreter.
    */
-  public String consumeInnerHtml(PostProcessingInterpreter<String> interpreter,
-      DomCursor domCursor)
+  public String consumeInnerHtml(PostProcessingInterpreter<String> interpreter)
       throws UnableToCompleteException {
-    String html = consumeInnerHtml((Interpreter<String>) interpreter, 
-        domCursor);
+    String html = consumeInnerHtml((Interpreter<String>) interpreter);
     return interpreter.postProcess(html);
   }
 
diff --git a/user/test/com/google/gwt/dom/client/ElementTest.java b/user/test/com/google/gwt/dom/client/ElementTest.java
index cb7dfc6..5806456 100644
--- a/user/test/com/google/gwt/dom/client/ElementTest.java
+++ b/user/test/com/google/gwt/dom/client/ElementTest.java
@@ -286,23 +286,6 @@
     assertEquals("foo", nodes.getItem(0).getInnerText());
     assertEquals("bar", nodes.getItem(1).getInnerText());
   }
-  
-  public void testGetNonTextElement() {
-    DivElement div = Document.get().createDivElement();
-    Text text1 = Document.get().createTextNode("my text");
-    DivElement innerDiv = Document.get().createDivElement();
-    Text text2 = Document.get().createTextNode(" ");
-    SpanElement span = Document.get().createSpanElement();
-    Text text3 = Document.get().createTextNode("my text2");
-    div.appendChild(text1);
-    div.appendChild(innerDiv);
-    div.appendChild(text2);
-    div.appendChild(span);
-    div.appendChild(text3);
-    
-    assertEquals(innerDiv, div.getNonTextChild(0));
-    assertEquals(span, div.getNonTextChild(1));
-  }
 
   public void testHasAttribute() {
     DivElement div = Document.get().createDivElement();
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
index 58e834c..e828dba 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -31,7 +31,6 @@
 import com.google.gwt.uibinder.elementparsers.StackLayoutPanelParserTest;
 import com.google.gwt.uibinder.elementparsers.TabLayoutPanelParserTest;
 import com.google.gwt.uibinder.elementparsers.UIObjectParserTest;
-import com.google.gwt.uibinder.rebind.DomCursorTest;
 import com.google.gwt.uibinder.rebind.FieldWriterOfGeneratedCssResourceTest;
 import com.google.gwt.uibinder.rebind.GwtResourceEntityResolverTest;
 import com.google.gwt.uibinder.rebind.HandlerEvaluatorTest;
@@ -52,7 +51,6 @@
     TestSuite suite = new TestSuite("UiBinder tests that require the JRE");
 
     // rebind
-    suite.addTestSuite(DomCursorTest.class);
     suite.addTestSuite(FieldWriterOfGeneratedCssResourceTest.class);
     suite.addTestSuite(GwtResourceEntityResolverTest.class);
     suite.addTestSuite(HandlerEvaluatorTest.class);
diff --git a/user/test/com/google/gwt/uibinder/client/UiBinderUtilTest.java b/user/test/com/google/gwt/uibinder/client/UiBinderUtilTest.java
index 1fbfe60..4eded61 100644
--- a/user/test/com/google/gwt/uibinder/client/UiBinderUtilTest.java
+++ b/user/test/com/google/gwt/uibinder/client/UiBinderUtilTest.java
@@ -97,10 +97,10 @@
   }
 
   private void findAndAssertTextBeforeFirstChild(Element div, String id,
-      String firstText) {    
-    Document.get().getBody().appendChild(div);
+      String firstText) {
+    UiBinderUtil.TempAttachment t = UiBinderUtil.attachToDom(div);
     Element child = Document.get().getElementById(id);
-    Document.get().getBody().removeChild(div);    
+    t.detach();
     assertStartsWith(child.getInnerHTML(), firstText + "<");
   }
 
@@ -146,9 +146,9 @@
       findAndAssertTextBeforeFirstChild(div, ableId, ableText);
       findAndAssertTextBeforeFirstChild(div, bakerId, bakerText);
       findAndAssertTextBeforeFirstChild(div, charlieId, charlieText);
-      Document.get().getBody().appendChild(div);
+      UiBinderUtil.TempAttachment t = UiBinderUtil.attachToDom(div);
       Element e = Document.get().getElementById(deltaId);
-      Document.get().getBody().removeChild(div);
+      t.detach();
       assertEquals(deltaText, e.getInnerText());
     } finally {
       // tearDown isn't reliable enough, e.g. doesn't fire when exceptions
diff --git a/user/test/com/google/gwt/uibinder/rebind/DomCursorTest.java b/user/test/com/google/gwt/uibinder/rebind/DomCursorTest.java
deleted file mode 100644
index 4a620a1..0000000
--- a/user/test/com/google/gwt/uibinder/rebind/DomCursorTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.uibinder.rebind;
-
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
-
-import junit.framework.TestCase;
-
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-
-/**
- * Test for DomCuror.
- */
-public class DomCursorTest extends TestCase {
-
-  private static final String PARENT = "parent";
-  
-  private UiBinderWriter writer;
-  private DomCursor cursor;
-  
-  @Override
-  public void setUp() {
-    writer = org.easymock.classextension.EasyMock.createMock(
-        UiBinderWriter.class);
-    org.easymock.classextension.EasyMock.expect(writer.getUniqueId()).andStubAnswer(new IAnswer<Integer>() {
-      private int nextId = 1;
-      public Integer answer() {
-        return nextId++;
-      }      
-    });
-    MessagesWriter message = new MessagesWriter("ui", null, "", "", "");
-    org.easymock.classextension.EasyMock.expect(writer.getMessages()).andStubReturn(message);
-    writer.addInitComment((String) EasyMock.notNull(), EasyMock.notNull());
-    org.easymock.classextension.EasyMock.expectLastCall().asStub();
-    cursor = new DomCursor(PARENT, writer);
-  }
-  
-  public void testAccessExpressions() throws Exception {    
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(parent, 0).cast()", 1);    
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(parent, 1).cast()", 2);
-    verifyInitAssignment(writer, 
-        "UiBinderUtil.getNonTextChild(intermediate2, 0).cast()", 3);
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(parent, 2).cast()", 4);    
-    org.easymock.classextension.EasyMock.replay(writer);
-    
-    // parent
-    cursor.visitChild(makeElement("div")); 
-    // parent's first child
-    assertEquals(intermediate(1), cursor.getAccessExpression());  
-
-    cursor.advanceChild(); 
-    assertEquals(intermediate(2), cursor.getAccessExpression());
-    // intermediate 2, parent's second child
-    XMLElement span = makeElement("span");  
-    cursor.visitChild(span);
-    // intermediate 3, intermediate 2's first child
-    assertEquals(intermediate(3), cursor.getAccessExpression());  
-    cursor.finishChild(span);
-    
-    cursor.advanceChild();
-    // intermediate 4, parent's third child
-    assertEquals(intermediate(4), cursor.getAccessExpression());
-    
-    org.easymock.classextension.EasyMock.verify(writer);
-  }
-  
-  public void testParagraphsWith() throws Exception {
-    writer.die((String) EasyMock.anyObject());
-    org.easymock.classextension.EasyMock.expectLastCall().andThrow(new UnableToCompleteException());
-    org.easymock.classextension.EasyMock.replay(writer);
-    
-    cursor.visitChild(makeElement("p"));
-    XMLElement span = makeElement("span");
-    cursor.visitChild(span);
-    cursor.finishChild(span);
-    cursor.visitChild(makeElement("div"));    
-    
-    try {
-      cursor.getAccessExpression();
-      fail("Expected exception about block elements inside paragraphs");
-    } catch (Exception e) {
-      // Expected
-    }
-    org.easymock.classextension.EasyMock.verify(writer);
-  }
-  
-  public void testTables() throws UnableToCompleteException {
-    
-    verifyInitAssignment(writer, intermediate(1), "UiBinderUtil.getNonTextChild", "parent", 0);
-    verifyInitAssignment(writer, "UiBinderUtil.getTableChild(intermediate1, 0).cast()", 2);    
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(intermediate2, 0).cast()", 3);    
-    verifyInitAssignment(writer, intermediate(4), "UiBinderUtil.getNonTextChild", "parent", 1);    
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(intermediate4, 0).cast()", 5);    
-    verifyInitAssignment(writer, "UiBinderUtil.getNonTextChild(intermediate5, 0).cast()", 6);
-    
-    org.easymock.classextension.EasyMock.replay(writer);
-    XMLElement div = makeElement("div");
-    cursor.visitChild(div);
-    
-    XMLElement table1 = makeElement("table");
-    cursor.visitChild(table1);
-    assertEquals(intermediate(2), cursor.getAccessExpression());
-    
-    XMLElement tr = makeElement("tr");
-    cursor.visitChild(tr);
-    assertEquals(intermediate(3), cursor.getAccessExpression());
-    
-    cursor.finishChild(tr);
-    cursor.finishChild(table1);
-    cursor.advanceChild();
-    XMLElement table2 = makeElement("table");
-    cursor.visitChild(table2);
-    
-    XMLElement tbody = makeElement("tbody");
-    cursor.visitChild(tbody);
-    cursor.finishChild(tbody);
-    assertEquals(intermediate(5), cursor.getAccessExpression());
-    
-    XMLElement tr2 = makeElement("tr");
-    cursor.visitChild(tr2);
-    assertEquals(intermediate(6), cursor.getAccessExpression());
-    cursor.finishChild(tr2);
-    
-    XMLElement td = makeElement("td");
-    try {
-      cursor.visitChild(td);
-      fail("Expected exception about tds inside tables without trs");
-    } catch (Exception e) {
-      // expected
-    }
-    org.easymock.classextension.EasyMock.verify(writer);
-  }
-
-  private String intermediate(int count) {
-    return "intermediate" + count;
-  }
-  
-  private XMLElement makeElement(String tag) {
-    NamedNodeMap attributes = EasyMock.createNiceMock(NamedNodeMap.class);
-    Element element = EasyMock.createNiceMock(Element.class);
-    EasyMock.expect(element.getLocalName()).andStubReturn(tag);
-    EasyMock.expect(element.getTagName()).andStubReturn(tag);
-    EasyMock.expect(element.getAttributes()).andStubReturn(attributes);
-    EasyMock.replay(element, attributes);
-    return new XMLElement(element, null, null, null, null, null);
-  }
-  
-  private void verifyInitAssignment(UiBinderWriter writer, String expr, int intermediateCount) {
-    writer.addInitStatement("com.google.gwt.dom.client.Node %s = %s;", 
-        "intermediate" + intermediateCount, expr);
-  }
-  
-  private void verifyInitAssignment(UiBinderWriter writer, String var, String method, String parent,
-      int index) {
-    writer.addInitStatement("com.google.gwt.dom.client.Node %s = %s(%s, %d);", var, method, parent,
-        index);
-  } 
-}