Seperated Decorated versions of Widgets from existing widgets.  The newly introduced widgets are DecoratedTabPanel, DecoratedTabBar, DecoratedStackPanel, and DecoratedPopupPanel.  Samples and tests have been updated to reflect the change.

Patch by: jlabanca
Review by: jgw (desk review)




git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2645 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
index 8fe6a20..211aafe 100644
--- a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
+++ b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
@@ -17,7 +17,7 @@
 
 import com.google.gwt.user.client.ui.AbstractImagePrototype;
 import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.StackPanel;
+import com.google.gwt.user.client.ui.DecoratedStackPanel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
@@ -42,7 +42,7 @@
   }
 
   private int nextHeaderIndex = 0;
-  private StackPanel stackPanel = new StackPanel();
+  private DecoratedStackPanel stackPanel = new DecoratedStackPanel();
 
   /**
    * Constructs a new shortcuts widget using the specified images.
diff --git a/samples/mail/src/com/google/gwt/sample/mail/public/Mail.css b/samples/mail/src/com/google/gwt/sample/mail/public/Mail.css
index 8128615..df8c836 100644
--- a/samples/mail/src/com/google/gwt/sample/mail/public/Mail.css
+++ b/samples/mail/src/com/google/gwt/sample/mail/public/Mail.css
@@ -79,98 +79,98 @@
   background: #ccc;
 }
 
-.gwt-StackPanel {
+.gwt-DecoratedStackPanel {
   width: 15em;
   border-bottom: 1px solid #666;
 }
-.gwt-StackPanel .lcaption {
+.gwt-DecoratedStackPanel .lcaption {
   width: 32px;
   padding: 0 0 4px 5px;
 }
-.gwt-StackPanel .rcaption {
+.gwt-DecoratedStackPanel .rcaption {
   padding: 0 0 4px 5px;
 }
-.gwt-StackPanel .gwt-StackPanelContent {
+.gwt-DecoratedStackPanel .gwt-StackPanelContent {
   border: 1px solid #666;
   border-bottom: 0px;
   background: white;
   padding: 2px 2px 10px 5px;
 }
-.gwt-StackPanelItem {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem {
   cursor: pointer;
   cursor: hand;
 }
-.gwt-StackPanelItem .topLeft,
-.gwt-StackPanelItem .topRight {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .topLeft,
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .topRight {
   width: 4px;
   height: 4px;
-	zoom: 1;
+  zoom: 1;
 }
-html>body .gwt-StackPanelItem .topLeft {
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem .topLeft {
   background: #c1eec8 url(leftCorner.gif) no-repeat;
   border-left: 1px solid #666;
 }
-html>body .gwt-StackPanelItem .topRight {
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem .topRight {
   background: #c1eec8 url(rightCorner.gif) no-repeat;
   border-right: 1px solid #666;
 }
-.gwt-StackPanelItem .topLeftInner,
-.gwt-StackPanelItem .topRightInner {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .topLeftInner,
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .topRightInner {
   width: 4px;
   height: 4px;
 }
-* html .gwt-StackPanelItem .topLeftInner {
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem .topLeftInner {
   overflow: hidden;
   border-left: 1px solid #666;
   background-color: #d3def6;
   filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='leftCorner.gif',sizingMethod='crop');
 }
-* html .gwt-StackPanelItem .topRightInner {
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem .topRightInner {
   overflow: hidden;
   border-right: 1px solid #666;
   background-color: #d3def6;
   filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='rightCorner.gif',sizingMethod='crop');
 }
-.gwt-StackPanelItem .topCenter {
-	background: #ddefde url(gradient.gif) repeat-x 0px 0px;
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .topCenter {
+  background: #ddefde url(gradient.gif) repeat-x 0px 0px;
 }
-.gwt-StackPanelItem .middleLeft {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .middleLeft {
   background: #d3def6 url(gradient.gif) repeat-x 0px -1px;
   border-left: 1px solid #666;
 }
-.gwt-StackPanelItem .middleLeftInner,
-.gwt-StackPanelItem .middleRightInner {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .middleLeftInner,
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .middleRightInner {
   width: 1px;
   height: 1px;
 }
-.gwt-StackPanelItem .middleRight {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .middleRight {
   background: #d3def6 url(gradient.gif) repeat-x 0px -1px;
   border-right: 1px solid #666;
 }
-.gwt-StackPanelItem .middleCenter {
+.gwt-DecoratedStackPanel .gwt-StackPanelItem .middleCenter {
   font-weight: bold;
   font-size: 1.3em;
   background: #d3def6 url(gradient.gif) repeat-x 0px -1px;
 }
-html>body .gwt-StackPanelItem-first .topRight,
-html>body .gwt-StackPanelItem-first .topLeft {
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .topRight,
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .topLeft {
   border: 0px;
   background-color: white;
 }
-html>body .gwt-StackPanelItem-below-selected .topLeft,
-html>body .gwt-StackPanelItem-below-selected .topRight {
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .topLeft,
+html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .topRight {
   background-color: white;
 }
-* html .gwt-StackPanelItem-first .topLeftInner,
-* html .gwt-StackPanelItem-first .topRightInner {
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .topLeftInner,
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .topRightInner {
   border: 0px;
   background-color: white;
 }
-* html .gwt-StackPanelItem-first .topLeftInner {
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .topLeftInner {
   padding-left: 1px;
 }
-* html .gwt-StackPanelItem-below-selected .topLeftInner,
-* html .gwt-StackPanelItem-below-selected .topRightInner {
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .topLeftInner,
+* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .topRightInner {
   background-color: white;
 }
 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
index b7d7124..eb746f2 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
@@ -20,11 +20,11 @@
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.user.client.ui.AbstractImagePrototype;
 import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.DecoratedStackPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HasVerticalAlignment;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.StackPanel;
 import com.google.gwt.user.client.ui.Tree;
 import com.google.gwt.user.client.ui.TreeImages;
 import com.google.gwt.user.client.ui.TreeItem;
@@ -34,10 +34,9 @@
 /**
  * Example file.
  * 
- * @gwt.CSS .gwt-StackPanel
- * @gwt.CSS .gwt-StackPanelItem
- * @gwt.CSS html>body .gwt-StackPanelItem
- * @gwt.CSS * html .gwt-StackPanelItem
+ * @gwt.CSS .gwt-DecoratedStackPanel
+ * @gwt.CSS html>body .gwt-DecoratedStackPanel
+ * @gwt.CSS * html .gwt-DecoratedStackPanel
  * @gwt.CSS .cw-StackPanelHeader
  */
 public class CwStackPanel extends ContentWidget {
@@ -141,7 +140,7 @@
     Images images = (Images) GWT.create(Images.class);
 
     // Create a new stack panel
-    StackPanel stackPanel = new StackPanel();
+    DecoratedStackPanel stackPanel = new DecoratedStackPanel();
     stackPanel.setWidth("200px");
 
     // Add links to Mail folders
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
index f4f05ca..10b4647 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
@@ -18,17 +18,17 @@
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
+import com.google.gwt.user.client.ui.DecoratedTabPanel;
 import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.TabPanel;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
  * Example file.
  * 
- * @gwt.CSS .gwt-TabBar
- * @gwt.CSS html>body .gwt-TabBar
- * @gwt.CSS * html .gwt-TabBar
+ * @gwt.CSS .gwt-DecoratedTabBar
+ * @gwt.CSS html>body .gwt-DecoratedTabBar
+ * @gwt.CSS * html .gwt-DecoratedTabBar
  * @gwt.CSS .gwt-TabPanel
  */
 public class CwTabPanel extends ContentWidget {
@@ -85,7 +85,7 @@
   @Override
   public Widget onInitialize() {
     // Create a tab panel
-    TabPanel tabPanel = new TabPanel();
+    DecoratedTabPanel tabPanel = new DecoratedTabPanel();
     tabPanel.setWidth("400px");
 
     // Enable the deck panel animation
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
index 66689fb..a2d4d9f 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
@@ -20,6 +20,7 @@
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.DecoratedPopupPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.PopupPanel;
@@ -32,6 +33,9 @@
  * @gwt.CSS .gwt-PopupPanel
  * @gwt.CSS html>body .gwt-PopupPanel
  * @gwt.CSS * html .gwt-PopupPanel
+ * @gwt.CSS .gwt-DecoratedPopupPanel
+ * @gwt.CSS html>body .gwt-DecoratedPopupPanel
+ * @gwt.CSS * html .gwt-DecoratedPopupPanel
  */
 public class CwBasicPopup extends ContentWidget {
   /**
@@ -87,9 +91,9 @@
   @Override
   public Widget onInitialize() {
     // Create a basic popup widget
-    final PopupPanel simplePopup = new PopupPanel(true);
+    final DecoratedPopupPanel simplePopup = new DecoratedPopupPanel(true);
     simplePopup.ensureDebugId("cwBasicPopup-simplePopup");
-    simplePopup.setWidth("128px");
+    simplePopup.setWidth("150px");
     simplePopup.setWidget(new HTML(
         constants.cwBasicPopupClickOutsideInstructions()));
 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/public/default/Showcase.css b/samples/showcase/src/com/google/gwt/sample/showcase/public/default/Showcase.css
index 3de650c..1e4a587 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/public/default/Showcase.css
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/public/default/Showcase.css
@@ -74,14 +74,7 @@
   background: url(images/hborder.png) repeat-x 0px -2003px;
   border-bottom: 2px solid #bbcdf3;
 }
-.Application-content-title .gwt-TabBarItem .top,
-.Application-content-title .gwt-TabBarItem .topLeft,
-.Application-content-title .gwt-TabBarItem .topRight,
-.Application-content-title .gwt-TabBarItem .middleLeft,
-.Application-content-title .gwt-TabBarItem .middleRight {
-  display: none;
-}
-.Application-content-title .gwt-TabBarItem .middleCenter {
+.Application-content-title .gwt-TabBarItem {
   background: none;
   padding: 0px;
 }
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratedPopupPanel.java b/user/src/com/google/gwt/user/client/ui/DecoratedPopupPanel.java
new file mode 100644
index 0000000..ce112a7
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DecoratedPopupPanel.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.user.client.Element;
+
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A {@link PopupPanel} that wraps its content in a 3x3 grid, which allows users
+ * to add rounded corners.
+ * </p>
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-DecoratedPopupPanel { the outside of the popup }</li>
+ * <li>.gwt-DecoratedPopupPanel .content { the wrapper around the content }</li>
+ * </ul>
+ * <p>
+ * The styles that apply to {@link DecoratorPanel} also apply to PopupPanel.
+ * </p>
+ */
+public class DecoratedPopupPanel extends PopupPanel {
+  private static final String DEFAULT_STYLENAME = "gwt-DecoratedPopupPanel";
+
+  /**
+   * The panel used to nine box the contents.
+   */
+  private DecoratorPanel decPanel = new DecoratorPanel();
+
+  /**
+   * Creates an empty decorated popup panel. A child widget must be added to it
+   * before it is shown.
+   */
+  public DecoratedPopupPanel() {
+    this(false);
+  }
+
+  /**
+   * Creates an empty decorated popup panel, specifying its "auto-hide"
+   * property.
+   * 
+   * @param autoHide <code>true</code> if the popup should be automatically
+   *          hidden when the user clicks outside of it
+   */
+  public DecoratedPopupPanel(boolean autoHide) {
+    this(autoHide, false);
+  }
+
+  /**
+   * Creates an empty decorated popup panel, specifying its "auto-hide" and
+   * "modal" properties.
+   * 
+   * @param autoHide <code>true</code> if the popup should be automatically
+   *          hidden when the user clicks outside of it
+   * @param modal <code>true</code> if keyboard or mouse events that do not
+   *          target the PopupPanel or its children should be ignored
+   */
+  public DecoratedPopupPanel(boolean autoHide, boolean modal) {
+    super(autoHide, modal);
+    decPanel.setStyleName("");
+    setStylePrimaryName(DEFAULT_STYLENAME);
+    super.setWidget(decPanel);
+    setStyleName(getContainerElement(), "content", false);
+    setStyleName(decPanel.getContainerElement(), "content", true);
+  }
+
+  @Override
+  public void clear() {
+    decPanel.clear();
+  }
+
+  @Override
+  public Widget getWidget() {
+    return decPanel.getWidget();
+  }
+
+  @Override
+  public Iterator<Widget> iterator() {
+    return decPanel.iterator();
+  }
+
+  @Override
+  public boolean remove(Widget w) {
+    return decPanel.remove(w);
+  }
+
+  @Override
+  public void setWidget(Widget w) {
+    decPanel.setWidget(w);
+    maybeUpdateSize();
+  }
+
+  /**
+   * Get a specific Element from the panel.
+   * 
+   * @param row the row index
+   * @param cell the cell index
+   * @return the Element at the given row and cell
+   */
+  protected Element getCellElement(int row, int cell) {
+    return decPanel.getCellElement(row, cell);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratedStackPanel.java b/user/src/com/google/gwt/user/client/ui/DecoratedStackPanel.java
new file mode 100644
index 0000000..ab849be
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DecoratedStackPanel.java
@@ -0,0 +1,80 @@
+/*
+ * 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.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * <p>
+ * A {@link StackPanel} that wraps each item in a 2x3 grid (six box), which
+ * allows users to add rounded corners.
+ * </p>
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-DecoratedStackPanel { the panel itself }</li>
+ * <li>.gwt-DecoratedStackPanel .gwt-StackPanelItem { unselected items }</li>
+ * <li>.gwt-DecoratedStackPanel .gwt-StackPanelItem-selected { selected items }</li>
+ * <li>.gwt-DecoratedStackPanel .gwt-StackPanelContent { the wrapper around the
+ * contents of the item }</li>
+ * <li>.gwt-DecoratedStackPanel .topLeft { top left corner of the item}</li>
+ * <li>.gwt-DecoratedStackPanel .topCenter { top center of the item}</li>
+ * <li>.gwt-DecoratedStackPanel .topRight { top right corner of the item}</li>
+ * <li>.gwt-DecoratedStackPanel .middleLeft { left side of the item }</li>
+ * <li>.gwt-DecoratedStackPanel .middleCenter { center of the item, where the
+ * item text resides }</li>
+ * <li>.gwt-DecoratedStackPanel .middleRight { right side of the item }</li>
+ * </ul>
+ */
+public class DecoratedStackPanel extends StackPanel {
+  public static final String DEFAULT_STYLENAME = "gwt-DecoratedStackPanel";
+
+  /**
+   * Creates an empty decorated stack panel.
+   */
+  public DecoratedStackPanel() {
+    super();
+    setStylePrimaryName(DEFAULT_STYLENAME);
+  }
+
+  @Override
+  Element createHeaderElem() {
+    // Create the table
+    Element table = DOM.createTable();
+    Element tbody = DOM.createTBody();
+    DOM.appendChild(table, tbody);
+    DOM.setStyleAttribute(table, "width", "100%");
+    DOM.setElementPropertyInt(table, "cellSpacing", 0);
+    DOM.setElementPropertyInt(table, "cellPadding", 0);
+
+    // Add the top row
+    DOM.appendChild(tbody, DecoratorPanel.createTR("top"));
+
+    // Add the middle row
+    DOM.appendChild(tbody, DecoratorPanel.createTR("middle"));
+
+    // Return the table
+    return table;
+  }
+
+  @Override
+  Element getHeaderTextElem(Element headerElem) {
+    Element tbody = DOM.getFirstChild(headerElem);
+    Element tr = DOM.getChild(tbody, 1);
+    Element td = DOM.getChild(tr, 1);
+    return DOM.getFirstChild(td);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratedTabBar.java b/user/src/com/google/gwt/user/client/ui/DecoratedTabBar.java
new file mode 100644
index 0000000..43acefe
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DecoratedTabBar.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * <p>
+ * A {@link TabBar} that wraps each tab in a 2x3 grid (six box), which allows
+ * users to add rounded corners.
+ * </p>
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-DecoratedTabBar { the tab bar itself }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarFirst { the left edge of the bar }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarRest { the right edge of the bar }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem { unselected tabs }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem-wrapper { table cell around tab }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem-selected { additional style for
+ * selected tabs } </li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem-wrapper-selected { table cell
+ * around selected tab }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .topLeft { top left corner of the
+ * tab}</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .topCenter { top center of the tab}</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .topRight { top right corner of the
+ * tab}</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .middleLeft { left side of the tab }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .middleCenter { center of the tab,
+ * where the tab text or widget resides }</li>
+ * <li>.gwt-DecoratedTabBar .gwt-TabBarItem .middleRight { right side of the
+ * tab }</li>
+ * </ul>
+ */
+public class DecoratedTabBar extends TabBar {
+  static String[] TAB_ROW_STYLES = {"top", "middle"};
+
+  static final String STYLENAME_DEFAULT = "gwt-DecoratedTabBar";
+
+  /**
+   * Creates an empty {@link DecoratedTabBar}.
+   */
+  public DecoratedTabBar() {
+    super();
+    setStylePrimaryName(STYLENAME_DEFAULT);
+  }
+
+  @Override
+  protected SimplePanel createTabTextWrapper() {
+    return new DecoratorPanel(TAB_ROW_STYLES, 1);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratedTabPanel.java b/user/src/com/google/gwt/user/client/ui/DecoratedTabPanel.java
new file mode 100644
index 0000000..5ec9fb8
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DecoratedTabPanel.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * A {@link TabPanel} that uses a {@link DecoratedTabBar} with rounded corners.
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-DecoratedTabPanel { the tab panel itself }</li>
+ * <li>.gwt-TabPanelBottom { the bottom section of the tab panel (the deck
+ * containing the widget) }</li>
+ * </ul>
+ * 
+ * <p>
+ * <h3>Example</h3>
+ * {@example com.google.gwt.examples.TabPanelExample}
+ * </p>
+ */
+public class DecoratedTabPanel extends TabPanel {
+  private static final String DEFAULT_STYLENAME = "gwt-DecoratedTabPanel";
+
+  public DecoratedTabPanel() {
+    setStylePrimaryName(DEFAULT_STYLENAME);
+    getTabBar().setStylePrimaryName(DecoratedTabBar.STYLENAME_DEFAULT);
+  }
+
+  @Override
+  protected SimplePanel createTabTextWrapper() {
+    return new DecoratorPanel(DecoratedTabBar.TAB_ROW_STYLES, 1);
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java b/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java
index 0ec52cf..2fdb849 100644
--- a/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java
@@ -162,11 +162,6 @@
     setStyleName(DEFAULT_STYLENAME);
   }
 
-  @Override
-  protected Element getContainerElement() {
-    return containerElem;
-  }
-
   /**
    * Get a specific Element from the panel.
    * 
@@ -174,8 +169,14 @@
    * @param cell the cell index
    * @return the Element at the given row and cell
    */
-  Element getCellElement(int row, int cell) {
+  protected Element getCellElement(int row, int cell) {
     Element tr = DOM.getChild(tbody, row);
-    return DOM.getChild(tr, cell);
+    Element td = DOM.getChild(tr, cell);
+    return DOM.getFirstChild(td);
+  }
+
+  @Override
+  protected Element getContainerElement() {
+    return containerElem;
   }
 }
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 d759046..a3d8b4d 100644
--- a/user/src/com/google/gwt/user/client/ui/DialogBox.java
+++ b/user/src/com/google/gwt/user/client/ui/DialogBox.java
@@ -41,7 +41,7 @@
  * {@example com.google.gwt.examples.DialogBoxExample}
  * </p>
  */
-public class DialogBox extends PopupPanel implements HasHTML, HasText,
+public class DialogBox extends DecoratedPopupPanel implements HasHTML, HasText,
     MouseListener {
   /**
    * The default style name.
@@ -201,6 +201,6 @@
   protected void onEnsureDebugId(String baseID) {
     super.onEnsureDebugId(baseID);
     caption.ensureDebugId(baseID + "-caption");
-    ensureDebugId(getContainerElement(), baseID, "content");
+    ensureDebugId(getCellElement(1, 1), baseID, "content");
   }
 }
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 002fcdf..5e55a79 100644
--- a/user/src/com/google/gwt/user/client/ui/MenuBar.java
+++ b/user/src/com/google/gwt/user/client/ui/MenuBar.java
@@ -517,7 +517,7 @@
     // Create a new popup for this item, and position it next to
     // the item (below if this is a horizontal menu bar, to the
     // right if it's a vertical bar).
-    popup = new PopupPanel(true) {
+    popup = new DecoratedPopupPanel(true) {
       {
         setWidget(item.getSubMenu());
         item.getSubMenu().onShow();
diff --git a/user/src/com/google/gwt/user/client/ui/PopupPanel.java b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
index c81c919..d223f64 100644
--- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -45,11 +45,8 @@
  * <li>.gwt-PopupPanel { the outside of the popup }</li>
  * <li>.gwt-PopupPanel .content { the wrapper around the content }</li>
  * </ul>
- * <p>
- * The styles that apply to {@link DecoratorPanel} also apply to PopupPanel.
- * </p>
  */
-public class PopupPanel extends DecoratorPanel implements SourcesPopupEvents,
+public class PopupPanel extends SimplePanel implements SourcesPopupEvents,
     EventPreview, HasAnimation {
   /**
    * The type of animation to use when opening the popup.
@@ -77,7 +74,7 @@
      * The {@link PopupPanel} being affected.
      */
     private PopupPanel curPanel = null;
-    
+
     /**
      * A boolean indicating whether we are showing or hiding the popup.
      */
@@ -95,6 +92,7 @@
         impl.onHide(curPanel.getElement());
       }
       impl.setClip(curPanel.getElement(), "rect(auto, auto, auto, auto)");
+      DOM.setStyleAttribute(curPanel.getElement(), "overflow", "visible");
       curPanel = null;
     }
 
@@ -113,6 +111,7 @@
         RootPanel.get().remove(curPanel);
         impl.onHide(curPanel.getElement());
       }
+      DOM.setStyleAttribute(curPanel.getElement(), "overflow", "visible");
       curPanel = null;
     }
 
@@ -133,6 +132,7 @@
       }
       offsetHeight = curPanel.getOffsetHeight();
       offsetWidth = curPanel.getOffsetWidth();
+      DOM.setStyleAttribute(curPanel.getElement(), "overflow", "hidden");
       onUpdate(0.0);
     }
 
@@ -270,7 +270,6 @@
     setPopupPosition(0, 0);
     setStyleName(DEFAULT_STYLENAME);
     setStyleName(getContainerElement(), "content");
-    DOM.setStyleAttribute(getElement(), "overflow", "hidden");
   }
 
   /**
@@ -285,7 +284,8 @@
   }
 
   /**
-   * Creates an empty popup panel, specifying its "auto-hide" property.
+   * Creates an empty popup panel, specifying its "auto-hide" and "modal"
+   * properties.
    * 
    * @param autoHide <code>true</code> if the popup should be automatically
    *          hidden when the user clicks outside of it
@@ -311,7 +311,7 @@
   public void center() {
     boolean initiallyShowing = showing;
     boolean initiallyAnimated = isAnimationEnabled;
-    
+
     if (!initiallyShowing) {
       setVisible(false);
       setAnimationEnabled(false);
@@ -667,6 +667,32 @@
   }
 
   /**
+   * We control size by setting our child widget's size. However, if we don't
+   * currently have a child, we record the size the user wanted so that when we
+   * do get a child, we can set it correctly. Until size is explicitly cleared,
+   * any child put into the popup will be given that size.
+   */
+  void maybeUpdateSize() {
+    // For subclasses of PopupPanel, we want the default behavior of setWidth
+    // and setHeight to change the dimensions of PopupPanel's child widget.
+    // We do this because PopupPanel's child widget is the first widget in
+    // the hierarchy which provides structure to the panel. DialogBox is
+    // an example of this. We want to set the dimensions on DialogBox's
+    // FlexTable, which is PopupPanel's child widget. However, it is not
+    // DialogBox's child widget. To make sure that we are actually getting
+    // PopupPanel's child widget, we have to use super.getWidget().
+    Widget w = super.getWidget();
+    if (w != null) {
+      if (desiredHeight != null) {
+        w.setHeight(desiredHeight);
+      }
+      if (desiredWidth != null) {
+        w.setWidth(desiredWidth);
+      }
+    }
+  }
+
+  /**
    * Remove focus from an Element.
    * 
    * @param elt The Element on which <code>blur()</code> will be invoked
@@ -694,30 +720,4 @@
       popupListeners.firePopupClosed(this, autoClosed);
     }
   }
-
-  /**
-   * We control size by setting our child widget's size. However, if we don't
-   * currently have a child, we record the size the user wanted so that when we
-   * do get a child, we can set it correctly. Until size is explicitly cleared,
-   * any child put into the popup will be given that size.
-   */
-  private void maybeUpdateSize() {
-    // For subclasses of PopupPanel, we want the default behavior of setWidth
-    // and setHeight to change the dimensions of PopupPanel's child widget.
-    // We do this because PopupPanel's child widget is the first widget in
-    // the hierarchy which provides structure to the panel. DialogBox is
-    // an example of this. We want to set the dimensions on DialogBox's
-    // FlexTable, which is PopupPanel's child widget. However, it is not
-    // DialogBox's child widget. To make sure that we are actually getting
-    // PopupPanel's child widget, we have to use super.getWidget().
-    Widget w = super.getWidget();
-    if (w != null) {
-      if (desiredHeight != null) {
-        w.setHeight(desiredHeight);
-      }
-      if (desiredWidth != null) {
-        w.setWidth(desiredWidth);
-      }
-    }
-  }
 }
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 2c18ac1..5a00409 100644
--- a/user/src/com/google/gwt/user/client/ui/StackPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/StackPanel.java
@@ -206,13 +206,11 @@
     }
 
     Element tdWrapper = DOM.getChild(DOM.getChild(body, index * 2), 0);
-    Element tbody = DOM.getFirstChild(DOM.getFirstChild(tdWrapper));
-    Element tr = DOM.getChild(tbody, 1);
-    Element td = DOM.getChild(tr, 1);
+    Element headerElem = DOM.getFirstChild(tdWrapper);
     if (asHTML) {
-      DOM.setInnerHTML(td, text);
+      DOM.setInnerHTML(getHeaderTextElem(headerElem), text);
     } else {
-      DOM.setInnerText(td, text);
+      DOM.setInnerText(getHeaderTextElem(headerElem), text);
     }
   }
 
@@ -251,40 +249,31 @@
 
     int numHeaders = DOM.getChildCount(body) >> 1;
     for (int i = 0; i < numHeaders; i++) {
-      Element headerElem = DOM.getFirstChild(DOM.getChild(body, 2 * i));
+      Element tdWrapper = DOM.getFirstChild(DOM.getChild(body, 2 * i));
+      Element headerElem = DOM.getFirstChild(tdWrapper);
       Element bodyElem = DOM.getFirstChild(DOM.getChild(body, 2 * i + 1));
-      ensureDebugId(headerElem, baseID, "text-wrapper" + i);
+      ensureDebugId(tdWrapper, baseID, "text-wrapper" + i);
       ensureDebugId(bodyElem, baseID, "content" + i);
-
-      // Set the ID on the inner most container of the stack item
-      Element table = DOM.getFirstChild(headerElem);
-      Element tbody = DOM.getFirstChild(DOM.getFirstChild(headerElem));
-      Element tr = DOM.getChild(tbody, 1);
-      Element td = DOM.getChild(tr, 1);
-      ensureDebugId(td, baseID, "text" + i);
+      ensureDebugId(getHeaderTextElem(headerElem), baseID, "text" + i);
     }
   }
 
   /**
    * @return a header element
    */
-  private Element createHeaderElem() {
-    // Create the table
-    Element table = DOM.createTable();
-    Element tbody = DOM.createTBody();
-    DOM.appendChild(table, tbody);
-    DOM.setStyleAttribute(table, "width", "100%");
-    DOM.setElementPropertyInt(table, "cellSpacing", 0);
-    DOM.setElementPropertyInt(table, "cellPadding", 0);
+  Element createHeaderElem() {
+    return DOM.createDiv();
+  }
 
-    // Add the top row
-    DOM.appendChild(tbody, DecoratorPanel.createTR("top"));
-
-    // Add the middle row
-    DOM.appendChild(tbody, DecoratorPanel.createTR("middle"));
-
-    // Return the table
-    return table;
+  /**
+   * Get the element that holds the header text given the header element created
+   * by #createHeaderElement.
+   * 
+   * @param headerElem the header element
+   * @return the element around the header text
+   */
+  Element getHeaderTextElem(Element headerElem) {
+    return headerElem;
   }
 
   private int findDividerIndex(Element elem) {
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 87cdbd4..9a4c338 100644
--- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -195,7 +195,7 @@
    * SuggestionMenu as its widget, and the position of the SuggestBox's TextBox
    * is needed in order to correctly position the popup.
    */
-  private class SuggestionPopup extends PopupPanel {
+  private class SuggestionPopup extends DecoratedPopupPanel {
 
     private static final String STYLENAME_DEFAULT = "gwt-SuggestBoxPopup";
 
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 943a2b1..d1e6a94 100644
--- a/user/src/com/google/gwt/user/client/ui/TabBar.java
+++ b/user/src/com/google/gwt/user/client/ui/TabBar.java
@@ -44,33 +44,38 @@
 public class TabBar extends Composite implements SourcesTabEvents,
     ClickListener, KeyboardListener {
   /**
-   * <code>ClickDecoratorPanel</code> decorates any widget with the minimal
+   * <code>ClickDelegatePanel</code> decorates any widget with the minimal
    * amount of machinery to receive clicks for delegation to the parent.
    * {@link SourcesClickEvents} is not implemented due to the fact that only a
    * single observer is needed.
    */
-  private static final class ClickDecoratorPanel extends DecoratorPanel {
-    /**
-     * The styles applied to the {@link DecoratorPanel}.  We only need to six
-     * box the tabs because we only expect to have rounded corners on the top.
-     */
-    private static String[] ROW_STYLES = {"top", "middle"};
+  private class ClickDelegatePanel extends Composite {
+    private SimplePanel focusablePanel;
+    private ClickListener clickDelegate;
+    private KeyboardListener keyDelegate;
 
-    ClickListener clickDelegate;
-    KeyboardListener keyDelegate;
-
-    ClickDecoratorPanel(Widget child, ClickListener cDelegate, KeyboardListener kDelegate) {
-      super(ROW_STYLES, 1);
+    ClickDelegatePanel(Widget child, ClickListener cDelegate,
+        KeyboardListener kDelegate) {
       this.clickDelegate = cDelegate;
       this.keyDelegate = kDelegate;
-      SimplePanel focusablePanel = new SimplePanel(FocusPanel.impl.createFocusable());
 
+      focusablePanel = new SimplePanel(FocusPanel.impl.createFocusable());
       focusablePanel.setWidget(child);
-      setWidget(focusablePanel);
+      SimplePanel wrapperWidget = createTabTextWrapper(); 
+      if (wrapperWidget == null) {
+        initWidget(focusablePanel);
+      } else {
+        wrapperWidget.setWidget(focusablePanel);
+        initWidget(wrapperWidget);
+      }
 
       sinkEvents(Event.ONCLICK | Event.ONKEYDOWN);
     }
 
+    public SimplePanel getFocusablePanel() {
+      return focusablePanel;
+    }
+
     @Override
     public void onBrowserEvent(Event event) {
       // No need for call to super.
@@ -185,16 +190,16 @@
     if (index >= getTabCount()) {
       return null;
     }
-    ClickDecoratorPanel decPanel = (ClickDecoratorPanel) panel.getWidget(index + 1);
-    SimplePanel focusablePanel = (SimplePanel) decPanel.getWidget();
+    ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1);
+    SimplePanel focusablePanel = delPanel.getFocusablePanel();
     Widget widget = focusablePanel.getWidget();
     if (widget instanceof HTML) {
       return ((HTML) widget).getHTML();
     } else if (widget instanceof Label) {
       return ((Label) widget).getText();
     } else {
-      // This will be a ClickDecorator holding a user-supplied widget.
-      return DOM.getInnerHTML(decPanel.getContainerElement());
+      // This will be a focusable panel holding a user-supplied widget.
+      return focusablePanel.getElement().getParentElement().getInnerHTML();
     }
   }
 
@@ -311,6 +316,17 @@
   }
 
   /**
+   * Create a {@link SimplePanel} that will wrap the contents in a tab.
+   * Subclasses can use this method to wrap tabs in decorator panels.
+   * 
+   * @return a {@link SimplePanel} to wrap the tab contents, or null to leave
+   *         tabs unwrapped
+   */
+  protected SimplePanel createTabTextWrapper() {
+    return null;
+  }
+
+  /**
    * Inserts a new tab at the specified index.
    * 
    * @param widget widget to be used in the new tab.
@@ -319,17 +335,17 @@
   protected void insertTabWidget(Widget widget, int beforeIndex) {
     checkInsertBeforeTabIndex(beforeIndex);
 
-    ClickDecoratorPanel decWidget = new ClickDecoratorPanel(widget, this, this);
-    decWidget.setStyleName(STYLENAME_DEFAULT);
+    ClickDelegatePanel delWidget = new ClickDelegatePanel(widget, this, this);
+    delWidget.setStyleName(STYLENAME_DEFAULT);
 
-     // Add a11y role "tab"
-    SimplePanel focusablePanel = (SimplePanel) decWidget.getWidget();
+    // Add a11y role "tab"
+    SimplePanel focusablePanel = delWidget.getFocusablePanel();
     Accessibility.setRole(focusablePanel.getElement(), Accessibility.ROLE_TAB);
-    
-    panel.insert(decWidget, beforeIndex + 1);
-    
-    setStyleName(DOM.getParent(decWidget.getElement()),
-        STYLENAME_DEFAULT + "-wrapper", true);
+
+    panel.insert(delWidget, beforeIndex + 1);
+
+    setStyleName(DOM.getParent(delWidget.getElement()), STYLENAME_DEFAULT
+        + "-wrapper", true);
   }
 
   /**
@@ -347,9 +363,10 @@
 
     int numTabs = getTabCount();
     for (int i = 0; i < numTabs; i++) {
-      DecoratorPanel decPanel = (DecoratorPanel) panel.getWidget(i + 1);
-      ensureDebugId(decPanel.getContainerElement(), baseID, "tab" + i);
-      ensureDebugId(DOM.getParent(decPanel.getElement()), baseID, "tab-wrapper"
+      ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(i + 1);
+      SimplePanel focusablePanel = delPanel.getFocusablePanel();
+      ensureDebugId(focusablePanel.getContainerElement(), baseID, "tab" + i);
+      ensureDebugId(DOM.getParent(delPanel.getElement()), baseID, "tab-wrapper"
           + i);
     }
   }
@@ -367,13 +384,13 @@
   }
 
   /**
-   * Selects the tab corresponding to the widget for the tab. To be clear
-   * the widget for the tab is not the widget INSIDE of the tab; it is the
-   * widget used to represent the tab itself.
-   *
+   * Selects the tab corresponding to the widget for the tab. To be clear the
+   * widget for the tab is not the widget INSIDE of the tab; it is the widget
+   * used to represent the tab itself.
+   * 
    * @param tabWidget The widget for the tab to be selected
-   * @return true if the tab corresponding to the widget for the tab could located and selected,
-   *         false otherwise
+   * @return true if the tab corresponding to the widget for the tab could
+   *         located and selected, false otherwise
    */
   private boolean selectTabByTabWidget(Widget tabWidget) {
     int numTabs = panel.getWidgetCount() - 1;
@@ -386,7 +403,7 @@
 
     return false;
   }
-   
+
   private void setSelectionStyle(Widget item, boolean selected) {
     if (item != null) {
       if (selected) {
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 743fffa..e58f78a 100644
--- a/user/src/com/google/gwt/user/client/ui/TabPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/TabPanel.java
@@ -48,7 +48,6 @@
  */
 public class TabPanel extends Composite implements TabListener,
     SourcesTabEvents, HasWidgets, IndexedPanel {
-
   /**
    * This extension of DeckPanel overrides the public mutator methods to prevent
    * external callers from adding to the state of the DeckPanel.
@@ -138,7 +137,7 @@
    * This extension of TabPanel overrides the public mutator methods to prevent
    * external callers from modifying the state of the TabBar.
    */
-  private static class UnmodifiableTabBar extends TabBar {
+  private class UnmodifiableTabBar extends TabBar {
     @Override
     public void insertTab(String text, boolean asHTML, int beforeIndex) {
       throw new UnsupportedOperationException(
@@ -151,6 +150,14 @@
           "Use TabPanel.insert() to alter the TabBar");
     }
 
+    public void insertTabProtected(String text, boolean asHTML, int beforeIndex) {
+      super.insertTab(text, asHTML, beforeIndex);
+    }
+
+    public void insertTabProtected(Widget widget, int beforeIndex) {
+      super.insertTab(widget, beforeIndex);
+    }
+
     @Override
     public void removeTab(int index) {
       // It's possible for removeTab() to function correctly, but it's
@@ -160,18 +167,14 @@
           "Use TabPanel.remove() to alter the TabBar");
     }
 
-    protected void insertTabProtected(String text, boolean asHTML,
-        int beforeIndex) {
-      super.insertTab(text, asHTML, beforeIndex);
-    }
-
-    protected void insertTabProtected(Widget widget, int beforeIndex) {
-      super.insertTab(widget, beforeIndex);
-    }
-
-    protected void removeTabProtected(int index) {
+    public void removeTabProtected(int index) {
       super.removeTab(index);
     }
+
+    @Override
+    protected SimplePanel createTabTextWrapper() {
+      return TabPanel.this.createTabTextWrapper();
+    }
   }
 
   private UnmodifiableTabBar tabBar = new UnmodifiableTabBar();
@@ -373,6 +376,17 @@
   }
 
   /**
+   * Create a {@link SimplePanel} that will wrap the contents in a tab.
+   * Subclasses can use this method to wrap tabs in decorator panels.
+   * 
+   * @return a {@link SimplePanel} to wrap the tab contents, or null to leave
+   *         tabs unwrapped
+   */
+  protected SimplePanel createTabTextWrapper() {
+    return null;
+  }
+
+  /**
    * <b>Affected Elements:</b>
    * <ul>
    * <li>-bar = The tab bar.</li>
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 1106526..c63ee5f 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -26,6 +26,10 @@
 import com.google.gwt.user.client.ui.CustomButtonTest;
 import com.google.gwt.user.client.ui.DOMTest;
 import com.google.gwt.user.client.ui.DeckPanelTest;
+import com.google.gwt.user.client.ui.DecoratedPopupTest;
+import com.google.gwt.user.client.ui.DecoratedStackPanelTest;
+import com.google.gwt.user.client.ui.DecoratedTabBarTest;
+import com.google.gwt.user.client.ui.DecoratedTabPanelTest;
 import com.google.gwt.user.client.ui.DecoratorPanelTest;
 import com.google.gwt.user.client.ui.DelegatingKeyboardListenerCollectionTest;
 import com.google.gwt.user.client.ui.DialogBoxTest;
@@ -85,6 +89,10 @@
     suite.addTestSuite(CookieTest.class);
     suite.addTestSuite(CustomButtonTest.class);
     suite.addTestSuite(DeckPanelTest.class);
+    suite.addTestSuite(DecoratedPopupTest.class);
+    suite.addTestSuite(DecoratedStackPanelTest.class);
+    suite.addTestSuite(DecoratedTabBarTest.class);
+    suite.addTestSuite(DecoratedTabPanelTest.class);
     suite.addTestSuite(DecoratorPanelTest.class);
     suite.addTestSuite(DelegatingKeyboardListenerCollectionTest.class);
     suite.addTestSuite(DialogBoxTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratedPopupTest.java b/user/test/com/google/gwt/user/client/ui/DecoratedPopupTest.java
new file mode 100644
index 0000000..bc20531
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DecoratedPopupTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Test cases for {@link DecoratedPopupPanel}.
+ */
+public class DecoratedPopupTest extends PopupTest {
+  @Override
+  protected PopupPanel createPopupPanel() {
+    return new DecoratedPopupPanel();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratedStackPanelTest.java b/user/test/com/google/gwt/user/client/ui/DecoratedStackPanelTest.java
new file mode 100644
index 0000000..0413276
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DecoratedStackPanelTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Test cases for {@link DecoratedStackPanel}.
+ */
+public class DecoratedStackPanelTest extends StackPanelTest {
+  @Override
+  protected StackPanel createStackPanel() {
+    return new DecoratedStackPanel();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratedTabBarTest.java b/user/test/com/google/gwt/user/client/ui/DecoratedTabBarTest.java
new file mode 100644
index 0000000..e1a9ac1
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DecoratedTabBarTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Test cases for {@link DecoratedTabBar}.
+ */
+public class DecoratedTabBarTest extends TabBarTest {
+  @Override
+  protected TabBar createTabBar() {
+    return new DecoratedTabBar();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratedTabPanelTest.java b/user/test/com/google/gwt/user/client/ui/DecoratedTabPanelTest.java
new file mode 100644
index 0000000..a623a26
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DecoratedTabPanelTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Tests the {@link DecoratedTabPanel}.
+ */
+public class DecoratedTabPanelTest extends TabPanelTest {
+  @Override
+  protected TabPanel createTabPanel() {
+    return new DecoratedTabPanel();
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java b/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java
index f0c478c..23187e7 100644
--- a/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java
@@ -59,14 +59,18 @@
     // Check the styles of each row
     for (int i = 0; i < rowStyles.length; i++) {
       String rowStyle = rowStyles[i];
-      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle);
-      assertStyleName(panel.getCellElement(i, 0), rowStyle + "Left");
-      assertStyleName(panel.getCellElement(i, 1), rowStyle + "Center");
-      assertStyleName(panel.getCellElement(i, 2), rowStyle + "Right");
+      assertStyleName(DOM.getParent(DOM.getParent(panel.getCellElement(i, 0))),
+          rowStyle);
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle
+          + "Left");
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 1)), rowStyle
+          + "Center");
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 2)), rowStyle
+          + "Right");
     }
 
     // Check the container element
-    assertTrue(DOM.getFirstChild(panel.getCellElement(2, 1)) == panel.getContainerElement());
+    assertTrue(panel.getCellElement(2, 1) == panel.getContainerElement());
   }
 
   /**
@@ -79,13 +83,17 @@
     // Check the styles of each row
     for (int i = 0; i < rowStyles.length; i++) {
       String rowStyle = rowStyles[i];
-      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle);
-      assertStyleName(panel.getCellElement(i, 0), rowStyle + "Left");
-      assertStyleName(panel.getCellElement(i, 1), rowStyle + "Center");
-      assertStyleName(panel.getCellElement(i, 2), rowStyle + "Right");
+      assertStyleName(DOM.getParent(DOM.getParent(panel.getCellElement(i, 0))),
+          rowStyle);
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle
+          + "Left");
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 1)), rowStyle
+          + "Center");
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 2)), rowStyle
+          + "Right");
     }
 
     // Check the container element
-    assertTrue(DOM.getFirstChild(panel.getCellElement(1, 1)) == panel.getContainerElement());
+    assertTrue(panel.getCellElement(1, 1) == panel.getContainerElement());
   }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/PopupTest.java b/user/test/com/google/gwt/user/client/ui/PopupTest.java
index ad989d5..d0e862f 100644
--- a/user/test/com/google/gwt/user/client/ui/PopupTest.java
+++ b/user/test/com/google/gwt/user/client/ui/PopupTest.java
@@ -43,8 +43,8 @@
    * Test the basic accessors.
    */
   public void testAccessors() {
-    PopupPanel popup = new PopupPanel();
-    
+    PopupPanel popup = createPopupPanel();
+
     // Animation enabled
     assertFalse(popup.isAnimationEnabled());
     popup.setAnimationEnabled(true);
@@ -55,7 +55,7 @@
     // Get rid of window margins so we can test absolute position.
     Window.setMargin("0px");
 
-    PopupPanel popup = new PopupPanel();
+    PopupPanel popup = createPopupPanel();
     popup.setAnimationEnabled(false);
     Label lbl = new Label("foo");
 
@@ -102,7 +102,7 @@
     });
     popup.hide();
   }
-  
+
   public void testSeparateContainers() {
     TestablePopupPanel p1 = new TestablePopupPanel();
     TestablePopupPanel p2 = new TestablePopupPanel();
@@ -110,4 +110,11 @@
     assertTrue(p2.getContainerElement() != null);
     assertFalse(p1.getContainerElement() == p2.getContainerElement());
   }
+
+  /**
+   * Create a new PopupPanel.
+   */
+  protected PopupPanel createPopupPanel() {
+    return new PopupPanel();
+  }
 }
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 d449523..925e0f9 100644
--- a/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/StackPanelTest.java
@@ -22,7 +22,7 @@
 import com.google.gwt.user.client.Element;
 
 /**
- * Tests <code>ListBox</code>. Needs many, many more tests.
+ * Test cases for {@link StackPanel}.
  */
 public class StackPanelTest extends GWTTestCase {
 
@@ -51,11 +51,11 @@
   }
 
   public void testAttachDetachOrder() {
-    HasWidgetsTester.testAll(new StackPanel(), new Adder());
+    HasWidgetsTester.testAll(createStackPanel(), new Adder());
   }
 
   public void testDebugId() {
-    final StackPanel p = new StackPanel();
+    final StackPanel p = createStackPanel();
     Label a = new Label("a");
     Label b = new Label("b");
     Label c = new Label("c");
@@ -86,6 +86,7 @@
         Element td0 = DOM.getElementById("gwt-debug-myStack-text-wrapper0");
         Element td1 = DOM.getElementById("gwt-debug-myStack-text-wrapper1");
         Element td2 = DOM.getElementById("gwt-debug-myStack-text-wrapper2");
+
         assertEquals(p.getElement(),
             DOM.getParent(DOM.getParent(DOM.getParent(td0))));
         assertEquals(p.getElement(),
@@ -93,6 +94,7 @@
         assertEquals(p.getElement(),
             DOM.getParent(DOM.getParent(DOM.getParent(td2))));
 
+        RootPanel.get().remove(p);
         finishTest();
       }
     });
@@ -103,7 +105,7 @@
    * Tests getSelectedStack.
    */
   public void testGetSelectedStack() {
-    StackPanel p = new StackPanel();
+    StackPanel p = createStackPanel();
     assertEquals(-1, p.getSelectedIndex());
     Label a = new Label("a");
     Label b = new Label("b");
@@ -124,7 +126,7 @@
    * Tests new remove implementation for StackPanel.
    */
   public void testRemove() {
-    StackPanel p = new StackPanel();
+    StackPanel p = createStackPanel();
     Label a = new Label("a");
     Label b = new Label("b");
     Label c = new Label("c");
@@ -154,4 +156,11 @@
     p.remove(a);
     assertEquals("d", curContents(p));
   }
+
+  /**
+   * Create a new stack panel.
+   */
+  protected StackPanel createStackPanel() {
+    return new StackPanel();
+  }
 }
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 c5c57b7..a84c5e9 100644
--- a/user/test/com/google/gwt/user/client/ui/TabBarTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabBarTest.java
@@ -23,22 +23,6 @@
  * TODO: document me.
  */
 public class TabBarTest extends GWTTestCase {
-
-  /**
-   * Get a specific grand parent of the Element.
-   *
-   * @param elem the element
-   * @param level the level above the element
-   * @return the grand parent
-   */
-  static Element getGrandParent(Element elem, int level) {
-    Element grandParent = elem;
-    for (int i = 0; i < level; i++) {
-      grandParent = DOM.getParent(grandParent);
-    }
-    return grandParent;
-  }
-  
   int selected;
   int beforeSelection;
 
@@ -48,7 +32,8 @@
   }
 
   public void testDebugId() {
-    TabBar bar = new TabBar();
+    // Create a tab bar with a few tabs
+    TabBar bar = createTabBar();
     Label tab0 = new Label("My Tab 0");
     bar.addTab(tab0);
     Label tab1 = new Label("My Tab 1");
@@ -56,22 +41,22 @@
     Label tab2 = new Label("My Tab 2");
     bar.addTab(tab2);
 
+    // Verify the debug IDs are on the correct elements
     bar.ensureDebugId("myBar");
+    HorizontalPanel hPanel = (HorizontalPanel) bar.getWidget();
+    Element tr = DOM.getFirstChild(hPanel.getBody());
     UIObjectTest.assertDebugId("myBar", bar.getElement());
-    UIObjectTest.assertDebugId("myBar-tab0", getGrandParent(tab0.getElement(), 2));
-    UIObjectTest.assertDebugId("myBar-tab-wrapper0", getGrandParent(
-        tab0.getElement(), 7));
-    UIObjectTest.assertDebugId("myBar-tab1", getGrandParent(tab1.getElement(), 2));
-    UIObjectTest.assertDebugId("myBar-tab-wrapper1", getGrandParent(
-        tab1.getElement(), 7));
-    UIObjectTest.assertDebugId("myBar-tab2", getGrandParent(tab2.getElement(), 2));
-    UIObjectTest.assertDebugId("myBar-tab-wrapper2", getGrandParent(
-        tab2.getElement(), 7));
+    UIObjectTest.assertDebugId("myBar-tab0", DOM.getParent(tab0.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper0", DOM.getChild(tr, 1));
+    UIObjectTest.assertDebugId("myBar-tab1", DOM.getParent(tab1.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper1", DOM.getChild(tr, 2));
+    UIObjectTest.assertDebugId("myBar-tab2", DOM.getParent(tab2.getElement()));
+    UIObjectTest.assertDebugId("myBar-tab-wrapper2", DOM.getChild(tr, 3));
   }
 
   public void testSelect() {
     // Create a tab bar with three items.
-    final TabBar bar = new TabBar();
+    final TabBar bar = createTabBar();
     bar.addTab("foo");
     bar.addTab("bar");
     bar.addTab("baz");
@@ -113,7 +98,7 @@
   }
 
   public void testGetHTML() {
-    final TabBar bar = new TabBar();
+    final TabBar bar = createTabBar();
     bar.addTab("foo");
     bar.addTab("<b>bar</b>", true);
     bar.addTab("baz");
@@ -122,4 +107,25 @@
     bar.removeTab(1);
     assertEquals("baz", bar.getTabHTML(1));
   }
+
+  /**
+   * Verify that if a tab contains a widget, {@link TabBar#getTabHTML(int)}
+   * returns the HTML of the focusable element with the widget inside of it.
+   */
+  public void testGetHTMLWithWidget() {
+    TabBar bar = createTabBar();
+    Button myButton = new Button("My Button");
+    bar.addTab(myButton);
+
+    Widget focusablePanel = myButton.getParent();
+    assertEquals(focusablePanel.getElement().getParentElement().getInnerHTML(),
+        bar.getTabHTML(0));
+  }
+
+  /**
+   * Create a new, empty tab bar.
+   */
+  protected TabBar createTabBar() {
+    return new TabBar();
+  }
 }
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 a912705..5e3e166 100644
--- a/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabPanelTest.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 import java.util.Iterator;
 
@@ -37,11 +38,11 @@
   }
 
   public void testAttachDetachOrder() {
-    HasWidgetsTester.testAll(new TabPanel(), new Adder());
+    HasWidgetsTester.testAll(createTabPanel(), new Adder());
   }
 
   public void testDebugId() {
-    TabPanel panel = new TabPanel();
+    TabPanel panel = createTabPanel();
     Label content0 = new Label("content0");
     Label tab0 = new Label("tab0");
     panel.add(content0, tab0);
@@ -60,22 +61,21 @@
         panel.getDeckPanel().getElement());
 
     // Check the tabs
+    HorizontalPanel hPanel = (HorizontalPanel) panel.getTabBar().getWidget();
+    Element tr = DOM.getFirstChild(hPanel.getBody());
     UIObjectTest.assertDebugId("myPanel-bar-tab0",
-        TabBarTest.getGrandParent(tab0.getElement(), 2));
-    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper0",
-        DOM.getParent(tab0.getParent().getParent().getElement()));
+        DOM.getParent(tab0.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper0", DOM.getChild(tr, 1));
     UIObjectTest.assertDebugId("myPanel-bar-tab1",
-        TabBarTest.getGrandParent(tab1.getElement(), 2));
-    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper1",
-        DOM.getParent(tab1.getParent().getParent().getElement()));
+        DOM.getParent(tab1.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper1", DOM.getChild(tr, 2));
     UIObjectTest.assertDebugId("myPanel-bar-tab2",
-        TabBarTest.getGrandParent(tab2.getElement(), 2));
-    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper2",
-        DOM.getParent(tab2.getParent().getParent().getElement()));
+        DOM.getParent(tab2.getElement()));
+    UIObjectTest.assertDebugId("myPanel-bar-tab-wrapper2", DOM.getChild(tr, 3));
   }
 
   public void testInsertMultipleTimes() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
 
     TextBox tb = new TextBox();
     p.add(tb, "Title");
@@ -114,7 +114,7 @@
   }
 
   public void testInsertWithHTML() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
     Label l = new Label();
     p.add(l, "three");
     p.insert(new HTML("<b>hello</b>"), "two", true, 0);
@@ -126,7 +126,7 @@
    * Tests to ensure that arbitrary widgets can be added/inserted effectively.
    */
   public void testInsertWithWidgets() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
 
     TextBox wa = new TextBox();
     CheckBox wb = new CheckBox();
@@ -146,7 +146,7 @@
   }
 
   public void testIterator() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
     HTML foo = new HTML("foo");
     HTML bar = new HTML("bar");
     HTML baz = new HTML("baz");
@@ -175,7 +175,7 @@
   }
 
   public void testSelectionEvents() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
     RootPanel.get().add(p);
 
     p.add(new Button("foo"), "foo");
@@ -201,7 +201,7 @@
   }
 
   public void testUnmodifiableDeckPanelSubclasses() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
     DeckPanel d = p.getDeckPanel();
 
     try {
@@ -227,7 +227,7 @@
   }
 
   public void testUnmodifiableTabBarSubclasses() {
-    TabPanel p = new TabPanel();
+    TabPanel p = createTabPanel();
     TabBar b = p.getTabBar();
 
     try {
@@ -279,4 +279,11 @@
       // Expected behavior
     }
   }
+
+  /**
+   * Create a new, empty tab panel.
+   */
+  protected TabPanel createTabPanel() {
+    return new TabPanel();
+  }
 }