This patch adds a new method "UIObject.ensureDebugID(String)", which will add an ID to a Widget and its important sub elements.  Widgets which have important sub elements, such as the tabs in TabBar, override the onEnsureDebugId method to set the IDs.

Issue: 1898
Patch by: jlabanca
Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1783 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/Debug.gwt.xml b/user/src/com/google/gwt/user/Debug.gwt.xml
new file mode 100644
index 0000000..c2426fc
--- /dev/null
+++ b/user/src/com/google/gwt/user/Debug.gwt.xml
@@ -0,0 +1,28 @@
+<!--                                                                        -->
+<!-- Copyright 2008 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<!-- Adds debug ID support for UIObjects.                                   -->
+<module>
+  <!-- Enable or disable the UIObject.ensureDebugID method -->
+  <define-property name="gwt.enableDebugId" values="true, false"/>
+  
+  <!-- Default to disabled -->
+  <set-property name="gwt.enableDebugId" value="true"/> 
+
+  <!-- Replace the DebugIdImpl -->
+  <replace-with class="com.google.gwt.user.client.ui.UIObject.DebugIdImplEnabled">
+    <when-type-is class="com.google.gwt.user.client.ui.UIObject.DebugIdImpl"/>
+    <when-property-is name="gwt.enableDebugId" value="true"/>
+  </replace-with>
+</module>
diff --git a/user/src/com/google/gwt/user/client/ui/CellPanel.java b/user/src/com/google/gwt/user/client/ui/CellPanel.java
index 873c781..6869b72 100644
--- a/user/src/com/google/gwt/user/client/ui/CellPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/CellPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -138,7 +138,7 @@
     DOM.setStyleAttribute(td, "verticalAlign", align.getVerticalAlignString());
   }
 
-  private Element getWidgetTd(Widget w) {
+  Element getWidgetTd(Widget w) {
     if (w.getParent() != this) {
       return null;
     }
diff --git a/user/src/com/google/gwt/user/client/ui/CheckBox.java b/user/src/com/google/gwt/user/client/ui/CheckBox.java
index 72762f5..e2826fb 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -179,6 +179,19 @@
   }
 
   /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-label => label next to checkbox</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    ensureDebugId(labelElem, baseID, "label");
+  }
+
+  /**
    * This method is called when a widget is attached to the browser's document.
    * onAttach needs special handling for the CheckBox case. Must still call
    * {@link Widget#onAttach()} to preserve the <code>onAttach</code> contract.
diff --git a/user/src/com/google/gwt/user/client/ui/DialogBox.java b/user/src/com/google/gwt/user/client/ui/DialogBox.java
index a98cfde..c8e847e 100644
--- a/user/src/com/google/gwt/user/client/ui/DialogBox.java
+++ b/user/src/com/google/gwt/user/client/ui/DialogBox.java
@@ -212,4 +212,19 @@
 
     child = w;
   }
+
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-caption => text at the top of the {@link DialogBox}</li>
+   * <li>-content => the table cell around the content</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    caption.ensureDebugId(baseID + "-caption");
+    ensureDebugId(panel.getCellFormatter().getElement(1, 0), baseID, "content");
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/DisclosurePanel.java b/user/src/com/google/gwt/user/client/ui/DisclosurePanel.java
index 4ccbb7a..62beed0 100644
--- a/user/src/com/google/gwt/user/client/ui/DisclosurePanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DisclosurePanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -393,6 +393,19 @@
     }
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-header => the clickable header</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    header.ensureDebugId(baseID + "-header");
+  }
+
   private void fireEvent() {
     if (handlers == null) {
       return;
diff --git a/user/src/com/google/gwt/user/client/ui/DockPanel.java b/user/src/com/google/gwt/user/client/ui/DockPanel.java
index e117679..c1c7ae4 100644
--- a/user/src/com/google/gwt/user/client/ui/DockPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DockPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -18,7 +18,9 @@
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * A panel that lays its child widgets out "docked" at its outer edges, and
@@ -85,6 +87,27 @@
    */
   public static final DockLayoutConstant WEST = new DockLayoutConstant();
 
+  /**
+   * Generate a debug ID for the {@link Widget} given the direction and number
+   * of occurrences of the direction.
+   * 
+   * @param direction the direction of the widget
+   * @param count the number of widgets in that direction
+   */
+  private static String generateDebugId(DockLayoutConstant direction, int count) {
+    if (direction == NORTH) {
+      return "north" + count;
+    } else if (direction == SOUTH) {
+      return "south" + count;
+    } else if (direction == WEST) {
+      return "west" + count;
+    } else if (direction == EAST) {
+      return "east" + count;
+    } else {
+      return "center";
+    }
+  }
+
   private HorizontalAlignmentConstant horzAlign = ALIGN_LEFT;
   private VerticalAlignmentConstant vertAlign = ALIGN_TOP;
   private Widget center;
@@ -234,6 +257,49 @@
   }
 
   /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * {@link DockPanel}s support adding more than one cell in a direction, so an
+   * integer will be appended to the end of the debug id. For example, the first
+   * north cell is labeled "north1", the second is "north2", and the third is
+   * "north3".
+   * 
+   * This widget recreates its structure every time a {@link Widget} is added,
+   * so you must call this method after adding new {@link Widget}s or all debug
+   * IDs will be lost.
+   * 
+   * <ul>
+   * <li>-center => the center cell</li>
+   * <li>-north# => the northern cell</li>
+   * <li>-south# => the southern cell</li>
+   * <li>-east# => the eastern cell</li>
+   * <li>-west# => the western cell</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+
+    Map<DockLayoutConstant, Integer> dirCount = new HashMap<DockLayoutConstant, Integer>();
+    Iterator<Widget> it = getChildren().iterator();
+    while (it.hasNext()) {
+      Widget child = it.next();
+      DockLayoutConstant dir = ((LayoutData) child.getLayoutData()).direction;
+
+      // Get a debug id
+      Integer count = dirCount.get(dir);
+      if (count == null) {
+        count = new Integer(1);
+      }
+      String debugID = generateDebugId(dir, count.intValue());
+      ensureDebugId(DOM.getParent(child.getElement()), baseID, debugID);
+
+      // Increment the count
+      dirCount.put(dir, count.intValue() + 1);
+    }
+  }
+
+  /**
    * (Re)creates the DOM structure of the table representing the DockPanel,
    * based on the order and layout of the children.
    */
diff --git a/user/src/com/google/gwt/user/client/ui/HTMLTable.java b/user/src/com/google/gwt/user/client/ui/HTMLTable.java
index 3cf6093..f622ed5 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLTable.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLTable.java
@@ -1294,6 +1294,27 @@
   }
 
   /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-(row)#-(cell)# => the cell at the given row and cell index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    
+    int rowCount = getRowCount();
+    for (int row = 0; row < rowCount; row++) {
+      int cellCount = getCellCount(row);
+      for (int cell = 0; cell < cellCount; cell++) {
+        Element cellElem = cellFormatter.getRawElement(row, cell);
+        ensureDebugId(cellElem, baseID, row + "-" + cell);
+      }
+    }
+  }
+
+  /**
    * Subclasses must implement this method. It allows them to decide what to do
    * just before a cell is accessed. If the cell already exists, this method
    * must do nothing. Otherwise, a subclass must either ensure that the cell
diff --git a/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java b/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
index 8c0e072..6d1fe8e 100644
--- a/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -120,6 +120,22 @@
     vertAlign = align;
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-# => the cell at the given index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    int numChildren = getWidgetCount();
+    for (int i = 0; i < numChildren; i++) {
+      ensureDebugId(getWidgetTd(getWidget(i)), baseID, "" + i);
+    }
+  }
+
   private Element createAlignedTd() {
     Element td = DOM.createTD();
     setCellHorizontalAlignment(td, horzAlign);
diff --git a/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java b/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
index b35c609..2603c2c 100644
--- a/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HorizontalSplitPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -320,6 +320,22 @@
     impl.setSplitPosition(getOffsetWidth(leftElem));
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-splitter => the container containing the splitter element</li>
+   * <li>-right => the container on the right side of the splitter</li>
+   * <li>-left => the container on the left side of the splitter</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    ensureDebugId(getElement(LEFT), baseID, "left");
+    ensureDebugId(getElement(RIGHT), baseID, "right");
+  }
+
   @Override
   protected void onLoad() {
     impl.onAttach();
diff --git a/user/src/com/google/gwt/user/client/ui/Hyperlink.java b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
index 4ef203c..5643ebf 100644
--- a/user/src/com/google/gwt/user/client/ui/Hyperlink.java
+++ b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -152,4 +152,17 @@
   public void setText(String text) {
     DOM.setInnerText(anchorElem, text);
   }
+
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-wrapper => the div around the link</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    ensureDebugId(anchorElem, "", baseID);
+    ensureDebugId(getElement(), baseID, "wrapper");
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/ListBox.java b/user/src/com/google/gwt/user/client/ui/ListBox.java
index b9af87f..fd43eee 100644
--- a/user/src/com/google/gwt/user/client/ui/ListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -79,6 +79,10 @@
     public native void setValue(Element select, int index, String value) /*-{
       select.options[index].value = value;
     }-*/;
+
+    protected native Element getItemElement(Element select, int index) /*-{
+      return select.options[index];
+    }-*/;
   }
 
   /**
@@ -135,6 +139,11 @@
     public native void setValue(Element select, int index, String value) /*-{
       select.children[index].value = value;
     }-*/;
+
+    @Override
+    protected native Element getItemElement(Element select, int index) /*-{
+      return select.children[index];
+    }-*/;
   }
 
   private static final int INSERT_AT_END = -1;
@@ -425,6 +434,25 @@
     DOM.setElementPropertyInt(getElement(), "size", visibleItems);
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-item# => the option at the specified index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+
+    // Set the id of each option
+    Element selectElem = getElement();
+    int numItems = getItemCount();
+    for (int i = 0; i < numItems; i++) {
+      ensureDebugId(impl.getItemElement(selectElem, i), baseID, "item" + i);
+    }
+  }
+
   private void checkIndex(int index) {
     if (index < 0 || index >= getItemCount()) {
       throw new IndexOutOfBoundsException();
diff --git a/user/src/com/google/gwt/user/client/ui/MenuBar.java b/user/src/com/google/gwt/user/client/ui/MenuBar.java
index 5bbfdd5..a717cf8 100644
--- a/user/src/com/google/gwt/user/client/ui/MenuBar.java
+++ b/user/src/com/google/gwt/user/client/ui/MenuBar.java
@@ -342,6 +342,19 @@
     super.onDetach();
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-item# => the {@link MenuItem} at the specified index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    setMenuItemDebugIds(baseID);
+  }
+
   /*
    * Closes all parent menu popups.
    */
@@ -484,6 +497,19 @@
   }
 
   /**
+   * Set the IDs of the menu items.
+   * 
+   * @param baseID the base ID
+   */
+  void setMenuItemDebugIds(String baseID) {
+    int itemCount = 0;
+    for (MenuItem item : items) {
+      item.ensureDebugId(baseID + "-item" + itemCount);
+      itemCount++;
+    }
+  }
+
+  /**
    * Physically add the td element of a {@link MenuItem} or
    * {@link MenuItemSeparator} to this {@link MenuBar}.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/MenuItem.java b/user/src/com/google/gwt/user/client/ui/MenuItem.java
index cf613a1..c23c2dd 100644
--- a/user/src/com/google/gwt/user/client/ui/MenuItem.java
+++ b/user/src/com/google/gwt/user/client/ui/MenuItem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -150,6 +150,20 @@
     DOM.setInnerText(getElement(), text);
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * Also sets the Debug IDs of {@link MenuItem}s in the submenu of this
+   * {@link MenuItem}, if one exists.
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    if (subMenu != null) {
+      subMenu.setMenuItemDebugIds(baseID);
+    }
+  }
+
   void setParentMenu(MenuBar parentMenu) {
     this.parentMenu = parentMenu;
   }
diff --git a/user/src/com/google/gwt/user/client/ui/SplitPanel.java b/user/src/com/google/gwt/user/client/ui/SplitPanel.java
index f1163e0..c881e6e 100644
--- a/user/src/com/google/gwt/user/client/ui/SplitPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/SplitPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -158,7 +158,7 @@
    * Convenience method to set the left offset of an element.
    * 
    * @param elem the element
-   * @param height a CSS length value for left
+   * @param left a CSS length value for left
    */
   static final void setLeft(Element elem, String left) {
     DOM.setStyleAttribute(elem, "left", left);
@@ -168,7 +168,7 @@
    * Convenience method to set the right offset of an element.
    * 
    * @param elem the element
-   * @param height a CSS length value for right
+   * @param right a CSS length value for right
    */
   static final void setRight(Element elem, String right) {
     DOM.setStyleAttribute(elem, "right", right);
@@ -178,7 +178,7 @@
    * Convenience method to set the top offset of an element.
    * 
    * @param elem the element
-   * @param height a CSS length value for top
+   * @param top a CSS length value for top
    */
   static final void setTop(Element elem, String top) {
     DOM.setStyleAttribute(elem, "top", top);
@@ -188,7 +188,7 @@
    * Convenience method to set the width of an element.
    * 
    * @param elem the element
-   * @param height a CSS length value for the width
+   * @param width a CSS length value for the width
    */
   static final void setWidth(Element elem, String width) {
     DOM.setStyleAttribute(elem, "width", width);
@@ -332,6 +332,19 @@
   }
 
   /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-splitter => the container containing the splitter element</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    ensureDebugId(splitElem, baseID, "splitter");
+  }
+
+  /**
    * Sets one of the contained widgets.
    * 
    * @param index the index, only 0 and 1 are valid
diff --git a/user/src/com/google/gwt/user/client/ui/StackPanel.java b/user/src/com/google/gwt/user/client/ui/StackPanel.java
index c5c7c77..6d874ba 100644
--- a/user/src/com/google/gwt/user/client/ui/StackPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/StackPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -225,6 +225,27 @@
     setStackVisible(visibleStack, true);
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-text# => The element around the header at the specified index</li>
+   * <li>-content# => The element around the body at the specified index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+
+    int numHeaders = DOM.getChildCount(body) / 2;
+    for (int i = 0; i < numHeaders; i++) {
+      Element headerElem = DOM.getFirstChild(DOM.getChild(body, 2 * i));
+      Element bodyElem = DOM.getFirstChild(DOM.getChild(body, 2 * i + 1));
+      ensureDebugId(headerElem, baseID, "text" + i);
+      ensureDebugId(bodyElem, baseID, "content" + i);
+    }
+  }
+
   private int findDividerIndex(Element elem) {
     while ((elem != null) && !DOM.compare(elem, getElement())) {
       String expando = DOM.getElementProperty(elem, "__index");
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
index d187e9b..4b125e2 100644
--- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -501,6 +501,21 @@
   }
 
   /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-popup => The popup that appears with suggestions</li>
+   * <li>-items-item# => The suggested item at the specified index</li>
+   * </ul> 
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    suggestionPopup.ensureDebugId(baseID + "-popup");
+    suggestionMenu.setMenuItemDebugIds(baseID);
+  }
+
+  /**
    * Show the given collection of suggestions.
    * 
    * @param suggestions suggestions to show
diff --git a/user/src/com/google/gwt/user/client/ui/TabBar.java b/user/src/com/google/gwt/user/client/ui/TabBar.java
index 1641c8c..bcebb91 100644
--- a/user/src/com/google/gwt/user/client/ui/TabBar.java
+++ b/user/src/com/google/gwt/user/client/ui/TabBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 
 /**
@@ -289,6 +290,26 @@
     return true;
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-tab# => The element containing the contents of the tab</li>
+   * <li>-tab-wrapper# => The cell containing the tab at the index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+
+    int numTabs = getTabCount();
+    for (int i = 0; i < numTabs; i++) {
+      Element widgetElem = panel.getWidget(i + 1).getElement();
+      ensureDebugId(widgetElem, baseID, "tab" + i);
+      ensureDebugId(DOM.getParent(widgetElem), baseID, "tab-wrapper" + i);
+    } 
+  }
+
   private void checkInsertBeforeTabIndex(int beforeIndex) {
     if ((beforeIndex < 0) || (beforeIndex > getTabCount())) {
       throw new IndexOutOfBoundsException();
diff --git a/user/src/com/google/gwt/user/client/ui/TabPanel.java b/user/src/com/google/gwt/user/client/ui/TabPanel.java
index 76ec8e5..1c6a9b0 100644
--- a/user/src/com/google/gwt/user/client/ui/TabPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/TabPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -132,7 +132,7 @@
       tabBar.insertTabProtected(tabWidget, beforeIndex);
       super.insert(w, beforeIndex);
     }
-  };
+  }
 
   /**
    * This extension of TabPanel overrides the public mutator methods to prevent
@@ -369,4 +369,21 @@
   public void selectTab(int index) {
     tabBar.selectTab(index);
   }
+
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-bar => The tab bar</li>
+   * <li>-bar-tab# => The element containing the contents of the tab itself</li>
+   * <li>-bar-tab#-wrapper => The cell containing the tab at the index</li>
+   * <li>-bottom => The panel beneath the tab bar</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    tabBar.ensureDebugId(baseID + "-bar");
+    deck.ensureDebugId(baseID + "-bottom");
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/Tree.java b/user/src/com/google/gwt/user/client/ui/Tree.java
index 387a024..37f596e 100644
--- a/user/src/com/google/gwt/user/client/ui/Tree.java
+++ b/user/src/com/google/gwt/user/client/ui/Tree.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -648,6 +648,19 @@
     return true;
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   *
+   * <ul>
+   * <li>-root => The root {@link TreeItem}</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    root.ensureDebugId(baseID + "-root");
+  }
+
   @Override
   protected void onLoad() {
     root.updateStateRecursive();
diff --git a/user/src/com/google/gwt/user/client/ui/TreeItem.java b/user/src/com/google/gwt/user/client/ui/TreeItem.java
index 4942b18..28481e8 100644
--- a/user/src/com/google/gwt/user/client/ui/TreeItem.java
+++ b/user/src/com/google/gwt/user/client/ui/TreeItem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -412,6 +412,28 @@
     }
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   *
+   * <ul>
+   * <li>-image => The status image</li>
+   * <li>-content => The text or {@link Widget} next to the image</li>
+   * <li>-child# => The child at the specified index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    statusImage.ensureDebugId(baseID + "-image");
+    ensureDebugId(contentElem, baseID, "content");
+
+    int childCount = 0;
+    for (TreeItem child : children) {
+      child.ensureDebugId(baseID + "-child" + childCount);
+      childCount++;
+    }
+  }
+
   void addTreeItems(List<TreeItem> accum) {
     for (int i = 0; i < children.size(); i++) {
       TreeItem item = children.get(i);
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java
index 7896c26..a778f3d 100644
--- a/user/src/com/google/gwt/user/client/ui/UIObject.java
+++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 
@@ -81,12 +82,35 @@
  * </p>
  */
 public abstract class UIObject {
+  /**
+   * The implementation of the set debug id method, which does nothing by
+   * default.
+   */
+  public static class DebugIdImpl {
+    public void ensureDebugId(UIObject uiObject, String id) {
+    }
+  }
+
+  /**
+   * The implementation of the setDebugId method, which sets the id of the
+   * {@link Element}s in this {@link UIObject}.
+   */
+  public static class DebugIdImplEnabled extends DebugIdImpl {
+    @Override
+    public void ensureDebugId(UIObject uiObject, String id) {
+      uiObject.onEnsureDebugId(id);
+    }
+  }
+
+  public static final String DEBUG_ID_PREFIX = "gwt-debug-";
 
   private static final String EMPTY_STYLENAME_MSG = "Style names cannot be empty";
 
   private static final String NULL_HANDLE_MSG = "Null widget handle. If you "
       + "are creating a composite, ensure that initWidget() has been called.";
 
+  private static DebugIdImpl debugIdImpl = GWT.create(DebugIdImpl.class);
+
   public static native boolean isVisible(Element elem) /*-{
     return (elem.style.display != 'none');
   }-*/;
@@ -96,6 +120,24 @@
   }-*/;
 
   /**
+   * Set the debug id of a specific element. The id will be appended to the end
+   * of the base debug id, with a dash separator. The base debug id is the ID of
+   * the main element in this UIObject.
+   * 
+   * @param elem the element
+   * @param baseID the base ID used by the main element
+   * @param id the id to append to the base debug id
+   */
+  protected static void ensureDebugId(Element elem, String baseID, String id) {
+    assert baseID != null;
+    String curID = DOM.getElementProperty(elem, "id");
+    if (curID.length() == 0 || curID.startsWith(DEBUG_ID_PREFIX)) {
+      baseID = (baseID.length() > 0) ? baseID + "-" : "";
+      DOM.setElementProperty(elem, "id", DEBUG_ID_PREFIX + baseID + id);
+    }
+  }
+
+  /**
    * Gets all of the element's style names, as a space-separated list.
    * 
    * @param elem the element whose style is to be retrieved
@@ -356,6 +398,29 @@
   }
 
   /**
+   * Ensure that the main {@link Element} for this {@link UIObject} has an ID
+   * property set, which allows it to integrate with third-party libraries and
+   * test tools. Complex {@link Widget}s will also set the IDs of their
+   * important sub-elements.
+   * 
+   * If the main element already has an ID, this method will NOT override it.
+   * The debugID is only used when no other ID is present on the {@link Element}.
+   * 
+   * The ID that you specify will be prefixed by the static string
+   * {@link #DEBUG_ID_PREFIX}.
+   * 
+   * This method will be compiled out and will have no effect unless you inherit
+   * the DebugID module in your gwt.xml file by adding the following line:
+   * 
+   * <inherits name="com.google.gwt.user.DebugID"/>
+   * 
+   * @param id the ID to set on the main element
+   */
+  public final void ensureDebugId(String id) {
+    debugIdImpl.ensureDebugId(this, id);
+  }
+
+  /**
    * Gets the object's absolute left position in pixels, as measured from the
    * browser window's client area.
    * 
@@ -625,6 +690,30 @@
   }
 
   /**
+   * Called when the user sets the id using the {@link #ensureDebugId(String)}
+   * method. Subclasses of {@link UIObject} can override this method to add IDs
+   * to their sub elements.  If a subclass does override this method, it should
+   * list the IDs (relative to the base ID), that will be applied to each sub
+   * {@link Element} with a short description.  For example:
+   * <ul>
+   * <li>-mysubelement => Applies to my sub element</li>
+   * </ul> 
+   * 
+   * Subclasses should make a super call to this method to ensure that the ID of
+   * the main element is set.
+   * 
+   * This method will not be called unless you inherit the DebugID module in
+   * your gwt.xml file by adding the following line:
+   * 
+   * <inherits name="com.google.gwt.user.DebugID"/>
+   * 
+   * @param baseID the base ID used by the main element
+   */
+  protected void onEnsureDebugId(String baseID) {
+    ensureDebugId(getElement(), "", baseID);
+  }
+
+  /**
    * Sets this object's browser element. UIObject subclasses must call this
    * method before attempting to call any other methods.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/VerticalPanel.java b/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
index 080117b..0145086 100644
--- a/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -122,6 +122,22 @@
     vertAlign = align;
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   *
+   * <ul>
+   * <li>-# => the cell at the given index</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    int numChildren = getWidgetCount();
+    for (int i = 0; i < numChildren; i++) {
+      ensureDebugId(getWidgetTd(getWidget(i)), baseID, "" + i);
+    }
+  }
+
   private Element createAlignedTd() {
     Element td = DOM.createTD();
     setCellHorizontalAlignment(td, horzAlign);
diff --git a/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java b/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
index feb51fa..144d85a 100644
--- a/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/VerticalSplitPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -326,6 +326,22 @@
     setWidget(TOP, w);
   }
 
+  /**
+   * @see UIObject#onEnsureDebugId(String)
+   * 
+   * <ul>
+   * <li>-splitter => the container containing the splitter element</li>
+   * <li>-top => the container above the splitter</li>
+   * <li>-bottom => the container below the splitter</li>
+   * </ul>
+   */
+  @Override
+  protected void onEnsureDebugId(String baseID) {
+    super.onEnsureDebugId(baseID);
+    ensureDebugId(getElement(TOP), baseID, "top");
+    ensureDebugId(getElement(BOTTOM), baseID, "bottom");
+  }
+
   @Override
   protected void onLoad() {
     impl.onAttach();
diff --git a/user/test/com/google/gwt/user/DebugTest.gwt.xml b/user/test/com/google/gwt/user/DebugTest.gwt.xml
new file mode 100644
index 0000000..1188f84
--- /dev/null
+++ b/user/test/com/google/gwt/user/DebugTest.gwt.xml
@@ -0,0 +1,18 @@
+<!--                                                                        -->
+<!-- Copyright 2008 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<module>
+  <inherits name="com.google.gwt.user.User"/>
+  <inherits name="com.google.gwt.user.Debug"/>
+</module>
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 6715d03..4551140 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -30,6 +30,7 @@
 import com.google.gwt.user.client.ui.HTMLPanelTest;
 import com.google.gwt.user.client.ui.HiddenTest;
 import com.google.gwt.user.client.ui.HorizontalPanelTest;
+import com.google.gwt.user.client.ui.HyperlinkTest;
 import com.google.gwt.user.client.ui.ImageTest;
 import com.google.gwt.user.client.ui.LinearPanelTest;
 import com.google.gwt.user.client.ui.ListBoxTest;
@@ -79,6 +80,7 @@
     // suite.addTestSuite(HistoryTest.class);
     suite.addTestSuite(HorizontalPanelTest.class);
     suite.addTestSuite(HTMLPanelTest.class);
+    suite.addTestSuite(HyperlinkTest.class);
     suite.addTestSuite(ImageTest.class);
     suite.addTestSuite(LinearPanelTest.class);
     suite.addTestSuite(ListBoxTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java b/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
index 81c355a..4182624 100644
--- a/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,14 +16,17 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests the CheckBox Widget.
  */
 public class CheckBoxTest extends GWTTestCase {
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   /**
@@ -53,4 +56,19 @@
     cb.setName("my name");
     assertEquals(cb.getName(), "my name");
   }
+
+  public void testDebugId() {
+    CheckBox check = new CheckBox("myLabel");
+    check.ensureDebugId("myCheck");
+    RootPanel.get().add(check);
+    
+    UIObjectTest.assertDebugId("myCheck", check.getElement());
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        UIObjectTest.assertDebugIdContents("myCheck-label", "myLabel");
+        finishTest();
+      }
+    });
+    delayTestFinish(250);
+  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
index daba9ed..bd6a696 100644
--- a/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DialogBoxTest.java
@@ -15,6 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
+
 /**
  * Unit test for {@link DialogBox}.
  */
@@ -22,7 +26,7 @@
 
   @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   /**
@@ -67,4 +71,29 @@
     dialogBox.setCaptionHTML("<b>text</b>");
     assertEquals("text", dialogBox.getText());
   }
+
+  public void testDebugId() {
+    DialogBox dBox = new DialogBox();
+    dBox.ensureDebugId("myDialogBox");
+    dBox.setText("test caption");
+    Label content = new Label("content");
+    dBox.setWidget(content);
+    dBox.show();
+
+    // Check the body ids
+    UIObjectTest.assertDebugId("myDialogBox", dBox.getElement());
+    UIObjectTest.assertDebugId("myDialogBox-content",
+        DOM.getParent(content.getElement()));
+
+    // Check the header IDs
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        String prefix = UIObject.DEBUG_ID_PREFIX;
+        UIObjectTest.assertDebugIdContents("myDialogBox-caption",
+            "test caption");
+        finishTest();
+      }
+    });
+    delayTestFinish(250);
+  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java b/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java
index dad3e93..939fed2 100644
--- a/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DisclosurePanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -26,8 +26,9 @@
 
   private static final int CLOSE = 1;
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   /**
@@ -48,6 +49,19 @@
     HasWidgetsTester.testAll(new DisclosurePanel());
   }
 
+  public void testDebugId() {
+    Label header = new Label("header");
+    Label content = new Label("content");
+    DisclosurePanel panel = new DisclosurePanel(header);
+    panel.setContent(content);
+    panel.ensureDebugId("myPanel");
+
+    // Check the body ids
+    UIObjectTest.assertDebugId("myPanel", panel.getElement());
+    UIObjectTest.assertDebugId("myPanel-header",
+        DOM.getParent(header.getElement()));
+  }
+
   /**
    * Test to ensure that event handler dispatch function appropriately.
    */
diff --git a/user/test/com/google/gwt/user/client/ui/DockPanelTest.java b/user/test/com/google/gwt/user/client/ui/DockPanelTest.java
index 2f50b10..19e8fcd 100644
--- a/user/test/com/google/gwt/user/client/ui/DockPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DockPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -37,6 +37,7 @@
       super(text);
     }
 
+    @Override
     protected void onLoad() {
       // Crawl up the DOM, looking for the body.
       Element curElem = getElement();
@@ -50,8 +51,9 @@
     }
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAddRemove() {
@@ -114,4 +116,48 @@
   public void testAttachDetachOrder() {
     HasWidgetsTester.testAll(new DockPanel(), new Adder());
   }
+
+  public void testDebugId() {
+    DockPanel dock = new DockPanel();
+    Label north1 = new Label("n1");
+    dock.add(north1, DockPanel.NORTH);
+    Label north2 = new Label("n2");
+    dock.add(north2, DockPanel.NORTH);
+    Label south1 = new Label("s1");
+    dock.add(south1, DockPanel.SOUTH);
+    Label south2 = new Label("s2");
+    dock.add(south2, DockPanel.SOUTH);
+    Label east1 = new Label("e1");
+    dock.add(east1, DockPanel.EAST);
+    Label east2 = new Label("e2");
+    dock.add(east2, DockPanel.EAST);
+    Label west1 = new Label("w1");
+    dock.add(west1, DockPanel.WEST);
+    Label west2 = new Label("w2");
+    dock.add(west2, DockPanel.WEST);
+    Label center = new Label("c");
+    dock.add(center, DockPanel.CENTER);
+    dock.ensureDebugId("myDock");
+
+    // Check the body ids
+    UIObjectTest.assertDebugId("myDock", dock.getElement());
+    UIObjectTest.assertDebugId("myDock-north1",
+        DOM.getParent(north1.getElement()));
+    UIObjectTest.assertDebugId("myDock-north2",
+        DOM.getParent(north2.getElement()));
+    UIObjectTest.assertDebugId("myDock-south1",
+        DOM.getParent(south1.getElement()));
+    UIObjectTest.assertDebugId("myDock-south2",
+        DOM.getParent(south2.getElement()));
+    UIObjectTest.assertDebugId("myDock-east1",
+        DOM.getParent(east1.getElement()));
+    UIObjectTest.assertDebugId("myDock-east2",
+        DOM.getParent(east2.getElement()));
+    UIObjectTest.assertDebugId("myDock-west1",
+        DOM.getParent(west1.getElement()));
+    UIObjectTest.assertDebugId("myDock-west2",
+        DOM.getParent(west2.getElement()));
+    UIObjectTest.assertDebugId("myDock-center",
+        DOM.getParent(center.getElement()));
+  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLTableTestBase.java b/user/test/com/google/gwt/user/client/ui/HTMLTableTestBase.java
index db00153..54ccdab 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLTableTestBase.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLTableTestBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 import com.google.gwt.user.client.ui.HTMLTable.RowFormatter;
 
@@ -43,7 +44,7 @@
   /**
    * Easy way to test what should be in a list.
    */
-  protected static void assertEquals(Object[] array, List target) {
+  protected static void assertEquals(Object[] array, List<?> target) {
     if (target.size() != array.length) {
       fail(target + " should be the same length as" + array);
     }
@@ -52,8 +53,9 @@
     }
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public abstract HTMLTable getTable(int row, int column);
@@ -72,6 +74,26 @@
     fail("should have throw an index out of bounds");
   }
 
+  public void testDebugId() {
+    HTMLTable table = getTable(4, 3);
+    for (int row = 0; row < 4; row++) {
+      for (int cell = 0; cell < 3; cell++) {
+        table.setHTML(row, cell, row + ":" + cell);
+      }
+    }
+
+    // Check the Debug IDs
+    table.ensureDebugId("myTable");
+    UIObjectTest.assertDebugId("myTable", table.getElement());
+    CellFormatter formatter = table.getCellFormatter();
+    for (int row = 0; row < 4; row++) {
+      for (int cell = 0; cell < 3; cell++) {
+        Element cellElem = formatter.getElement(row, cell); 
+        UIObjectTest.assertDebugId("myTable-" + row + "-" + cell, cellElem);
+      }
+    }
+  }
+
   public void testDoubleSet() {
     HTMLTable t = getTable(4, 4);
     t.setWidget(0, 0, new Label());
@@ -85,30 +107,30 @@
     HTMLTable t = getTable(5, 5);
     Label l = new Label("hello");
     t.setWidget(0, 0, l);
-    Iterator iter = t.iterator();
+    Iterator<Widget> iter = t.iterator();
     assertEquals(l, iter.next());
     iter.remove();
-    Iterator iter2 = t.iterator();
+    Iterator<Widget> iter2 = t.iterator();
     assertFalse(iter2.hasNext());
 
     // Check put after remove.
     Widget w = new Label("bo");
     t.setWidget(0, 0, w);
-    Iterator iter3 = t.iterator();
+    Iterator<Widget> iter3 = t.iterator();
     assertEquals(w, iter3.next());
     assertFalse(iter3.hasNext());
 
     // Check swapping widgets.
     Widget w2 = new Label("ba");
     t.setWidget(0, 0, w2);
-    Iterator iter4 = t.iterator();
+    Iterator<Widget> iter4 = t.iterator();
     assertEquals(w2, iter4.next());
     assertFalse(iter4.hasNext());
 
     // Check put after put.
     Widget w3 = new Label("be");
     t.setWidget(1, 1, w3);
-    Iterator iter5 = t.iterator();
+    Iterator<Widget> iter5 = t.iterator();
     assertEquals(w2, iter5.next());
     assertEquals(w3, iter5.next());
     assertFalse(iter5.hasNext());
diff --git a/user/test/com/google/gwt/user/client/ui/HorizontalPanelTest.java b/user/test/com/google/gwt/user/client/ui/HorizontalPanelTest.java
index 9846399..c9f3131 100644
--- a/user/test/com/google/gwt/user/client/ui/HorizontalPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HorizontalPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
 
 import java.util.Iterator;
 
@@ -24,14 +25,31 @@
  */
 public class HorizontalPanelTest extends GWTTestCase {
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAttachDetachOrder() {
     HasWidgetsTester.testAll(new HorizontalPanel());
   }
 
+  public void testDebugId() {
+    HorizontalPanel p = new HorizontalPanel();
+    Label a = new Label("a");
+    Label b = new Label("b");
+    Label c = new Label("c");
+    p.add(a);
+    p.add(b);
+    p.add(c);
+
+    p.ensureDebugId("myPanel");
+    UIObjectTest.assertDebugId("myPanel", p.getElement());
+    UIObjectTest.assertDebugId("myPanel-0", DOM.getParent(a.getElement()));
+    UIObjectTest.assertDebugId("myPanel-1", DOM.getParent(b.getElement()));
+    UIObjectTest.assertDebugId("myPanel-2", DOM.getParent(c.getElement()));
+  }
+
   public void testInsertMultipleTimes() {
     HorizontalPanel p = new HorizontalPanel();
 
@@ -42,7 +60,7 @@
 
     assertEquals(1, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(tb));
-    Iterator i = p.iterator();
+    Iterator<Widget> i = p.iterator();
     assertTrue(i.hasNext());
     assertTrue(tb.equals(i.next()));
     assertFalse(i.hasNext());
diff --git a/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java b/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java
new file mode 100644
index 0000000..0cff670
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
+
+/**
+ * Tests {@link HyperlinkTest}.
+ */
+public class HyperlinkTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.DebugTest";
+  }
+
+  public void testDebugId() {
+    Hyperlink link = new Hyperlink("Click Me", "myToken");
+    link.ensureDebugId("myLink");
+    UIObjectTest.assertDebugId("myLink-wrapper", link.getElement());
+    UIObjectTest.assertDebugId("myLink", DOM.getFirstChild(link.getElement()));
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
index b204b63..5b116de 100644
--- a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,14 +16,17 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests {@link ListBox}. Needs many, many more tests.
  */
 public class ListBoxTest extends GWTTestCase {
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testClear() {
@@ -35,6 +38,29 @@
     assertEquals(0, lb.getItemCount());
   }
 
+  public void testDebugId() {
+    ListBox list = new ListBox();
+    list.addItem("option0", "value0");
+    list.addItem("option1", "value1");
+    list.addItem("option2", "value2");
+    list.addItem("option3", "value3");
+    RootPanel.get().add(list);
+
+    list.ensureDebugId("myList");
+    UIObjectTest.assertDebugId("myList", list.getElement());
+
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        UIObjectTest.assertDebugIdContents("myList-item0", "option0");   
+        UIObjectTest.assertDebugIdContents("myList-item1", "option1");   
+        UIObjectTest.assertDebugIdContents("myList-item2", "option2");   
+        UIObjectTest.assertDebugIdContents("myList-item3", "option3");
+        finishTest();
+      }
+    });
+    delayTestFinish(250);
+  }
+
   public void testInsert() {
 
     // Insert in the middle
diff --git a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
index 7ee2468..acdf590 100644
--- a/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
+++ b/user/test/com/google/gwt/user/client/ui/MenuBarTest.java
@@ -17,6 +17,7 @@
 

 import com.google.gwt.junit.client.GWTTestCase;

 import com.google.gwt.user.client.Command;

+import com.google.gwt.user.client.DeferredCommand;

 

 import java.util.List;

 

@@ -26,7 +27,7 @@
 public class MenuBarTest extends GWTTestCase {

   @Override

   public String getModuleName() {

-    return "com.google.gwt.user.User";

+    return "com.google.gwt.user.DebugTest";

   }

 

   /**

@@ -90,4 +91,46 @@
     assertNull(separator2.getParentMenu());

     assertNull(separator3.getParentMenu());

   }

+

+  public void testDebugId() {

+    Command emptyCommand = new Command() {

+      public void execute() {

+      }

+    };

+

+    // Create a sub menu

+    MenuBar subMenu = new MenuBar(true);

+    subMenu.addItem("sub0", emptyCommand);

+    subMenu.addItem("sub1", emptyCommand);

+    subMenu.addItem("sub2", emptyCommand);

+

+    // Create a menu bar

+    MenuBar bar = new MenuBar(false);

+    bar.setAutoOpen(true);

+    bar.addItem("top0", emptyCommand);

+    bar.addItem("top1", emptyCommand);

+    MenuItem top2 = bar.addItem("top2", subMenu);

+    RootPanel.get().add(bar);

+    

+    // Open the item with a submenu

+    bar.itemOver(top2);

+

+    // Set the Debug Id

+    bar.ensureDebugId("myMenu");

+    UIObjectTest.assertDebugId("myMenu", bar.getElement());

+

+    DeferredCommand.addCommand(new Command() {

+      public void execute() {

+        UIObjectTest.assertDebugIdContents("myMenu-item0", "top0");

+        UIObjectTest.assertDebugIdContents("myMenu-item1", "top1");

+        UIObjectTest.assertDebugIdContents("myMenu-item2", "top2");

+

+        UIObjectTest.assertDebugIdContents("myMenu-item2-item0", "sub0");

+        UIObjectTest.assertDebugIdContents("myMenu-item2-item1", "sub1");

+        UIObjectTest.assertDebugIdContents("myMenu-item2-item2", "sub2");

+        finishTest();

+      }

+    });

+    delayTestFinish(250);

+  }

 }

diff --git a/user/test/com/google/gwt/user/client/ui/SplitPanelTest.java b/user/test/com/google/gwt/user/client/ui/SplitPanelTest.java
index 76e3f11..cb627f6 100644
--- a/user/test/com/google/gwt/user/client/ui/SplitPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/SplitPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -30,8 +30,9 @@
     return label;
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testHorizontalAttachDetachOrder() {
@@ -80,6 +81,28 @@
     assertEquals(100, panel.getOffsetHeight());
   }
 
+  public void testDebugId() {
+    VerticalSplitPanel vSplit = new VerticalSplitPanel();
+    vSplit.ensureDebugId("vsplit");
+    Label top = new Label("top");
+    vSplit.setTopWidget(top);
+    Label bottom = new Label("bottom");
+    vSplit.setBottomWidget(bottom);
+    UIObjectTest.assertDebugId("vsplit", vSplit.getElement());
+    UIObjectTest.assertDebugId("vsplit-top", DOM.getParent(top.getElement()));
+    UIObjectTest.assertDebugId("vsplit-bottom", DOM.getParent(bottom.getElement()));
+    
+    HorizontalSplitPanel hSplit = new HorizontalSplitPanel();
+    hSplit.ensureDebugId("hsplit");
+    Label left = new Label("left");
+    hSplit.setLeftWidget(left);
+    Label right = new Label("right");
+    hSplit.setRightWidget(right);
+    UIObjectTest.assertDebugId("hsplit", hSplit.getElement());
+    UIObjectTest.assertDebugId("hsplit-left", DOM.getParent(left.getElement()));
+    UIObjectTest.assertDebugId("hsplit-right", DOM.getParent(right.getElement()));
+  }
+
   public void testVerticalAttachDetachOrder() {
     HasWidgetsTester.testAll(new VerticalSplitPanel());
   }
diff --git a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
index de4d7ac..d824d1b 100644
--- a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,9 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
 
 /**
  * Tests <code>ListBox</code>. Needs many, many more tests.
@@ -28,8 +31,9 @@
     }
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public String curContents(StackPanel p) {
@@ -49,6 +53,37 @@
     HasWidgetsTester.testAll(new StackPanel(), new Adder());
   }
 
+  public void testDebugId() {
+    StackPanel p = new StackPanel();
+    Label a = new Label("a");
+    Label b = new Label("b");
+    Label c = new Label("c");
+    p.add(a, "header a");
+    p.add(b, "header b");
+    p.add(c, "header c");
+    RootPanel.get().add(p);
+
+    p.ensureDebugId("myStack");
+    
+    // Check the body ids
+    UIObjectTest.assertDebugId("myStack", p.getElement());
+    UIObjectTest.assertDebugId("myStack-content0", DOM.getParent(a.getElement()));
+    UIObjectTest.assertDebugId("myStack-content1", DOM.getParent(b.getElement()));
+    UIObjectTest.assertDebugId("myStack-content2", DOM.getParent(c.getElement()));
+    
+    // Check the header IDs
+    DeferredCommand.addCommand(new Command() {
+      public void execute() {
+        String prefix = UIObject.DEBUG_ID_PREFIX;
+        UIObjectTest.assertDebugIdContents("myStack-text0", "header a");
+        UIObjectTest.assertDebugIdContents("myStack-text1", "header b");
+        UIObjectTest.assertDebugIdContents("myStack-text2", "header c");
+        finishTest();
+      }
+    });
+    delayTestFinish(250);
+  }
+
   /**
    * Tests getSelectedStack.
    */
diff --git a/user/test/com/google/gwt/user/client/ui/TabBarTest.java b/user/test/com/google/gwt/user/client/ui/TabBarTest.java
index 6219fe3..3f1efb6 100644
--- a/user/test/com/google/gwt/user/client/ui/TabBarTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabBarTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
 
 /**
  * TODO: document me.
@@ -25,8 +26,31 @@
   int selected;
   int beforeSelection;
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
+  }
+
+  public void testDebugId() {
+    TabBar bar = new TabBar();
+    Label tab0 = new Label("My Tab 0");
+    bar.addTab(tab0);
+    Label tab1 = new Label("My Tab 1");
+    bar.addTab(tab1);
+    Label tab2 = new Label("My Tab 2");
+    bar.addTab(tab2);
+
+    bar.ensureDebugId("myBar");
+    UIObjectTest.assertDebugId("myBar", bar.getElement());
+    UIObjectTest.assertDebugId("myBar-tab0", DOM.getParent(tab0.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper0",
+        DOM.getParent(DOM.getParent(tab0.getElement())));
+    UIObjectTest.assertDebugId("myBar-tab1", DOM.getParent(tab1.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper1",
+        DOM.getParent(DOM.getParent(tab1.getElement())));
+    UIObjectTest.assertDebugId("myBar-tab2", DOM.getParent(tab2.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper2",
+        DOM.getParent(DOM.getParent(tab2.getElement())));
   }
 
   public void testSelect() {
diff --git a/user/test/com/google/gwt/user/client/ui/TabPanelTest.java b/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
index 0fde59e..a8bdc6b 100644
--- a/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
 
 import java.util.Iterator;
 
@@ -30,14 +31,49 @@
     }
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAttachDetachOrder() {
     HasWidgetsTester.testAll(new TabPanel(), new Adder());
   }
-  
+
+  public void testDebugId() {
+    TabPanel panel = new TabPanel();
+    Label content0 = new Label("content0");
+    Label tab0 = new Label("tab0");
+    panel.add(content0, tab0);
+    Label content1 = new Label("content1");
+    Label tab1 = new Label("tab1");
+    panel.add(content1, tab1);
+    Label content2 = new Label("content2");
+    Label tab2 = new Label("tab2");
+    panel.add(content2, tab2);
+
+    // Set the Debug ID
+    panel.ensureDebugId("myPanel");
+    UIObjectTest.assertDebugId("myPanel", panel.getElement());
+    UIObjectTest.assertDebugId("myPanel-bar", panel.getTabBar().getElement());
+    UIObjectTest.assertDebugId("myPanel-bottom",
+        panel.getDeckPanel().getElement());
+
+    // Check the tabs
+    UIObjectTest.assertDebugId("myPanel-bar-tab0",
+        DOM.getParent(tab0.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper0",
+        DOM.getParent(DOM.getParent(tab0.getElement())));
+    UIObjectTest.assertDebugId("myPanel-bar-tab1",
+        DOM.getParent(tab1.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper1",
+        DOM.getParent(DOM.getParent(tab1.getElement())));
+    UIObjectTest.assertDebugId("myPanel-bar-tab2",
+        DOM.getParent(tab2.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper2",
+        DOM.getParent(DOM.getParent(tab2.getElement())));
+  }
+
   public void testInsertMultipleTimes() {
     TabPanel p = new TabPanel();
 
@@ -45,14 +81,14 @@
     p.add(tb, "Title");
     p.add(tb, "Title");
     p.add(tb, "Title3");
-    
+
     assertEquals(1, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(tb));
-    Iterator i = p.iterator();
+    Iterator<Widget> i = p.iterator();
     assertTrue(i.hasNext());
     assertTrue(tb.equals(i.next()));
     assertFalse(i.hasNext());
-    
+
     Label l = new Label();
     p.add(l, "Title");
     p.add(l, "Title");
@@ -60,12 +96,12 @@
     assertEquals(2, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(tb));
     assertEquals(1, p.getWidgetIndex(l));
-    
+
     p.insert(l, "Title", 0);
     assertEquals(2, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(l));
     assertEquals(1, p.getWidgetIndex(tb));
-    
+
     p.insert(l, "Title", 1);
     assertEquals(2, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(l));
@@ -119,7 +155,7 @@
     p.add(baz, "baz");
 
     // Iterate over the entire set and make sure it stops correctly.
-    Iterator it = p.iterator();
+    Iterator<Widget> it = p.iterator();
     assertTrue(it.hasNext());
     assertTrue(it.next() == foo);
     assertTrue(it.hasNext());
@@ -167,14 +203,14 @@
   public void testUnmodifiableDeckPanelSubclasses() {
     TabPanel p = new TabPanel();
     DeckPanel d = p.getDeckPanel();
-    
+
     try {
       d.add(new Label("No"));
       fail("Internal DeckPanel should not allow add() method");
     } catch (UnsupportedOperationException e) {
       // Expected behavior
     }
-    
+
     try {
       d.insert(new Label("No"), 0);
       fail("Internal DeckPanel should not allow insert() method");
@@ -193,7 +229,7 @@
   public void testUnmodifiableTabBarSubclasses() {
     TabPanel p = new TabPanel();
     TabBar b = p.getTabBar();
-    
+
     try {
       b.addTab("no");
       fail("Internal TabBar should not allow addTab() method");
@@ -207,7 +243,7 @@
     } catch (UnsupportedOperationException e) {
       // Expected behavior
     }
-    
+
     try {
       b.addTab(new Label("no"));
       fail("Internal TabBar should not allow addTab() method");
@@ -221,21 +257,21 @@
     } catch (UnsupportedOperationException e) {
       // Expected behavior
     }
-    
+
     try {
       b.insertTab("no", true, 0);
       fail("Internal TabBar should not allow insertTab() method");
     } catch (UnsupportedOperationException e) {
       // Expected behavior
     }
-    
+
     try {
       b.insertTab(new Label("no"), 0);
       fail("Internal TabBar should not allow insertTab() method");
     } catch (UnsupportedOperationException e) {
       // Expected behavior
     }
-                                
+
     try {
       b.removeTab(0);
       fail("Internal TabBar should not allow removeTab() method");
diff --git a/user/test/com/google/gwt/user/client/ui/TreeTest.java b/user/test/com/google/gwt/user/client/ui/TreeTest.java
index 8053e8f..6f08a37 100644
--- a/user/test/com/google/gwt/user/client/ui/TreeTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TreeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -32,8 +32,9 @@
     }
   }
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAttachDetachOrder() {
@@ -64,6 +65,35 @@
     assertEquals(0, t.getChildWidgets().size());
   }
 
+  public void testDebugId() {
+    Tree tree = new Tree();
+    TreeItem top0 = tree.addItem("top0");
+    TreeItem top1 = tree.addItem("top1");
+    TreeItem top2 = tree.addItem("top2");
+    TreeItem top3 = tree.addItem("top3");
+    TreeItem bottom0 = top3.addItem("bottom0");
+    TreeItem bottom1 = top3.addItem("bottom1");
+    TreeItem bottom2 = top3.addItem("bottom2");
+
+    // Check tree items deep
+    tree.ensureDebugId("myTree");
+    UIObjectTest.assertDebugId("myTree", tree.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child0", top0.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child1", top1.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child2", top2.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child3", top3.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child3-child0",
+        bottom0.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child3-child1",
+        bottom1.getElement());
+    UIObjectTest.assertDebugId("myTree-root-child3-child2",
+        bottom2.getElement());
+    
+    // Check tree item sub elements
+    UIObjectTest.assertDebugId("myTree-root-child0-content", top0.getContentElem());
+    UIObjectTest.assertDebugId("myTree-root-child0-image", top0.getImageElement());
+  }
+
   public void testInsertSameItemRepeatedly() {
     Tree t = new Tree();
     TreeItem ti = new TreeItem();
@@ -80,13 +110,13 @@
 
   public void testIterator() {
     Tree tree = new Tree();
-    Iterator iter = tree.treeItemIterator();
+    Iterator<TreeItem> iter = tree.treeItemIterator();
     assertFalse(iter.hasNext());
     TreeItem a = tree.addItem("a");
     TreeItem b = tree.addItem("b");
     TreeItem c = tree.addItem("c");
 
-    Iterator iter2 = tree.treeItemIterator();
+    Iterator<TreeItem> iter2 = tree.treeItemIterator();
     assertEquals(a, iter2.next());
     assertEquals(b, iter2.next());
     assertEquals(c, iter2.next());
@@ -96,7 +126,7 @@
     TreeItem a_a_a = a_a.addItem("a_a_a");
     TreeItem a_a_b = a_a.addItem("a_a_b");
 
-    Iterator iter3 = tree.treeItemIterator();
+    Iterator<TreeItem> iter3 = tree.treeItemIterator();
     assertEquals(a, iter3.next());
     assertEquals(a_a, iter3.next());
     assertEquals(a_a_a, iter3.next());
@@ -127,13 +157,13 @@
     assertEquals(item, t.getSelectedItem());
     item.remove();
     assertNull(t.getSelectedItem());
-    Iterator iter = t.treeItemIterator();
+    Iterator<TreeItem> iter = t.treeItemIterator();
     assertTrue(iter.hasNext());
     iter.next();
     assertFalse(iter.hasNext());
     t.removeItem(itemb);
     assertNull(t.getSelectedItem());
-    Iterator iter2 = t.treeItemIterator();
+    Iterator<TreeItem> iter2 = t.treeItemIterator();
     assertFalse(iter2.hasNext());
   }
 
diff --git a/user/test/com/google/gwt/user/client/ui/UIObjectTest.java b/user/test/com/google/gwt/user/client/ui/UIObjectTest.java
index 86ad0d6..9961fe3 100644
--- a/user/test/com/google/gwt/user/client/ui/UIObjectTest.java
+++ b/user/test/com/google/gwt/user/client/ui/UIObjectTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -17,20 +17,58 @@
 
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * Tests UIObject. Currently, focuses on style name behaviors.
  */
 public class UIObjectTest extends GWTTestCase {
-
   static class MyObject extends UIObject {
+    public Element subElement;
+
     MyObject() {
       setElement(DOM.createDiv());
+      subElement = DOM.createDiv();
+    }
+
+    @Override
+    protected void onEnsureDebugId(String baseID) {
+      super.onEnsureDebugId(baseID);
+      ensureDebugId(subElement, baseID, "subElem");
     }
   }
 
+  /**
+   * Verify that an element has the specified debug id.
+   * 
+   * @param debugID the debug ID
+   * @param elem the {@link Element} that should have the id
+   */
+  public static void assertDebugId(String debugID, Element elem) {
+    debugID = UIObject.DEBUG_ID_PREFIX + debugID;
+    assertEquals(debugID, DOM.getElementProperty(elem, "id"));
+  }
+
+  /**
+   * Verify that the contents of an element match the expected contents. This
+   * method is useful to test debug IDs of private, inaccessible members of a
+   * Widget. Note that this method requires that the Widget is added to the
+   * {@link RootPanel} and should be called from a
+   * {@link com.google.gwt.user.client.DeferredCommand} to give the browser
+   * enough time to register the ID.
+   * 
+   * @param debugID the debug ID of the element
+   * @param contents the contents expected in the inner HTML
+   */
+  public static void assertDebugIdContents(String debugID, String contents) {
+    debugID = UIObject.DEBUG_ID_PREFIX + debugID;
+    Element elem = DOM.getElementById(debugID);
+    assertEquals(contents, DOM.getInnerHTML(elem));
+  }
+
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAccidentalPrimary() {
@@ -74,6 +112,30 @@
     assertEquals("primary", o.getStylePrimaryName());
   }
 
+  public void testDebugId() {
+    // Test basic set
+    MyObject o = new MyObject();
+    Element oElem = o.getElement();
+    o.ensureDebugId("test1");
+    assertDebugId("test1", oElem);
+    assertDebugId("test1-subElem", o.subElement);
+
+    // Test override with new ID
+    o.ensureDebugId("test2");
+    assertDebugId("test2", oElem);
+    assertDebugId("test2-subElem", o.subElement);
+
+    // Test setting actual id
+    DOM.setElementProperty(oElem, "id", "mytest");
+    assertEquals("mytest", DOM.getElementProperty(oElem, "id"));
+    assertDebugId("test2-subElem", o.subElement);
+
+    // Test overriding with debug ID fails if ID present
+    o.ensureDebugId("test3");
+    assertEquals("mytest", DOM.getElementProperty(oElem, "id"));
+    assertDebugId("test3-subElem", o.subElement);
+  }
+
   public void testNormal() {
     // Test the basic set/get case.
     MyObject o = new MyObject();
diff --git a/user/test/com/google/gwt/user/client/ui/VerticalPanelTest.java b/user/test/com/google/gwt/user/client/ui/VerticalPanelTest.java
index e7074ee..add59b3 100644
--- a/user/test/com/google/gwt/user/client/ui/VerticalPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/VerticalPanelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
 
 import java.util.Iterator;
 
@@ -24,14 +25,31 @@
  */
 public class VerticalPanelTest extends GWTTestCase {
 
+  @Override
   public String getModuleName() {
-    return "com.google.gwt.user.User";
+    return "com.google.gwt.user.DebugTest";
   }
 
   public void testAttachDetachOrder() {
     HasWidgetsTester.testAll(new VerticalPanel());
   }
 
+  public void testDebugId() {
+    VerticalPanel p = new VerticalPanel();
+    Label a = new Label("a");
+    Label b = new Label("b");
+    Label c = new Label("c");
+    p.add(a);
+    p.add(b);
+    p.add(c);
+
+    p.ensureDebugId("myPanel");
+    UIObjectTest.assertDebugId("myPanel", p.getElement());
+    UIObjectTest.assertDebugId("myPanel-0", DOM.getParent(a.getElement()));
+    UIObjectTest.assertDebugId("myPanel-1", DOM.getParent(b.getElement()));
+    UIObjectTest.assertDebugId("myPanel-2", DOM.getParent(c.getElement()));
+  }
+
   public void testInsertMultipleTimes() {
     VerticalPanel p = new VerticalPanel();
 
@@ -42,7 +60,7 @@
 
     assertEquals(1, p.getWidgetCount());
     assertEquals(0, p.getWidgetIndex(tb));
-    Iterator i = p.iterator();
+    Iterator<Widget> i = p.iterator();
     assertTrue(i.hasNext());
     assertTrue(tb.equals(i.next()));
     assertFalse(i.hasNext());