Fixes HTMLPanel to keep it from yanking it out of the DOM whenever a widget is added to it.
Issue: 1741
Patch by: jgw, knorton
Review by: knorton
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2134 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
index fb7e18e..3e6a458 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
@@ -80,10 +80,10 @@
}
/**
- * Performs a {@link DOM#getElementById(String)} after attaching the widget's
+ * Performs a {@link DOM#getElementById(String)} after attaching the panel's
* element into a hidden DIV in the document's body. Attachment is necessary
- * to be able to use the native getElementById. The hidden DIV will remain
- * attached to the DOM until the Widget itself is fully attached.
+ * to be able to use the native getElementById. The panel's element will be
+ * re-attached to its original parent (if any) after the method returns.
*
* @param id the id whose associated element is to be retrieved
* @return the associated element, or <code>null</code> if none is found
@@ -93,11 +93,25 @@
if (hiddenDiv == null) {
hiddenDiv = DOM.createDiv();
UIObject.setVisible(hiddenDiv, false);
- DOM.appendChild(hiddenDiv, getElement());
DOM.appendChild(RootPanel.getBodyElement(), hiddenDiv);
}
+ // Hang on to the panel's original parent and sibling elements so that it
+ // can be replaced.
+ Element origParent = DOM.getParent(getElement());
+ Element origSibling = DOM.getNextSibling(getElement());
+
+ // Attach the panel's element to the hidden div.
+ DOM.appendChild(hiddenDiv, getElement());
+
// Now that we're attached to the DOM, we can use getElementById.
- return DOM.getElementById(id);
+ Element child = DOM.getElementById(id);
+
+ // Put the panel's element back where it was.
+ if (origParent != null) {
+ DOM.insertBefore(origParent, getElement(), origSibling);
+ }
+
+ return child;
}
}
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
index 74205e4..72494ec 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
@@ -48,8 +48,54 @@
DOM.getParent(labelB.getElement()), "id"));
}
+ /**
+ * This is meant to catch an issue created by a faulty optimization. To
+ * optimize add() when the HTMLPanel is unattached, we would originally
+ * move its element to a hidden div so that getElementById() would work.
+ * Unfortunately, we didn't move it back to its original parent, causing
+ * a problem in the case described in this test.
+ */
+ public void testAddPartiallyAttached() {
+ SimplePanel sp = new SimplePanel();
+ HTMLPanel p = new HTMLPanel("<div id='foo'></div>");
+
+ // Add the HTMLPanel to another panel before adding the button.
+ sp.add(p);
+
+ // Add a button the HTMLPanel, causing the panel's element to be attached
+ // to the DOM.
+ p.add(new Button("foo"), "foo");
+
+ // If all goes well, the HTMLPanel's element should still be properly
+ // connected to the SimplePanel's element.
+ assertTrue(DOM.isOrHasChild(sp.getElement(), p.getElement()));
+ }
+
public void testAttachDetachOrder() {
HTMLPanel p = new HTMLPanel("<div id='w00t'></div>");
HasWidgetsTester.testAll(p, new Adder());
}
+
+ /**
+ * Ensures that attachToDomAndGetElement() puts the HTMLPanel back exactly
+ * where it was in the DOM originally.
+ */
+ public void testAttachDoesntMangleChildOrder() {
+ FlowPanel fp = new FlowPanel();
+
+ Button ba = new Button("before");
+ Button bb = new Button("after");
+
+ HTMLPanel hp = new HTMLPanel("<div id='foo'></div>");
+
+ fp.add(ba);
+ fp.add(hp);
+ fp.add(bb);
+
+ hp.add(new Button("foo"), "foo");
+
+ assertTrue(DOM.isOrHasChild(fp.getElement(), hp.getElement()));
+ assertTrue(DOM.getNextSibling(ba.getElement()) == hp.getElement());
+ assertTrue(DOM.getNextSibling(hp.getElement()) == bb.getElement());
+ }
}