Fixes issue 792 by adding guards to detatch, also throws IllegalStateException if we ever try to attach when not attached, or detach when not detached.
Review by: jgw
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@945 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index 2d4a2ee..5211ea2 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -75,10 +75,12 @@
* It must not be overridden, except by {@link Panel}. To receive
* notification when a widget is attached to the document, override the
* {@link #onLoad} method.
+ * @throws IllegalStateException if this widget is already attached
*/
protected void onAttach() {
if (attached) {
- return;
+ throw new IllegalStateException(
+ "Should only call onAttach when the widget is detached from the browser's document");
}
attached = true;
@@ -95,10 +97,13 @@
/**
* This method is called when a widget is detached from the browser's
* document. It must not be overridden, except by {@link Panel}.
+ *
+ * @throws IllegalStateException if this widget is already detached
*/
protected void onDetach() {
if (!attached) {
- return;
+ throw new IllegalStateException(
+ "Should only call onDetach when the widget is attached to the browser's document");
}
attached = false;
@@ -116,32 +121,31 @@
}
/**
- * Sets this object's browser element. Widget subclasses must call this
- * method before attempting to call any other methods.
- *
- * If a browser element has already been attached, then it is replaced with
- * the new element. The old event listeners are removed from the old browser
- * element, and the event listeners are set up on the new browser element.
- *
- * @param elem the object's new element
- */
- protected void setElement(Element elem) {
- if (attached) {
- // Remove old event listener to avoid leaking. onDetach will not do this
- // for us, because it is only called the widget itself is detached from
- // the document.
- DOM.setEventListener(getElement(), null);
- }
+ * Sets this object's browser element. Widget subclasses must call this method
+ * before attempting to call any other methods.
+ *
+ * If a browser element has already been attached, then it is replaced with
+ * the new element. The old event listeners are removed from the old browser
+ * element, and the event listeners are set up on the new browser element.
+ *
+ * @param elem the object's new element
+ */
+ protected void setElement(Element elem) {
+ if (attached) {
+ // Remove old event listener to avoid leaking. onDetach will not do this
+ // for us, because it is only called when the widget itself is detached from
+ // the document.
+ DOM.setEventListener(getElement(), null);
+ }
- super.setElement(elem);
-
- if (attached) {
- // Hook the event listener back up on the new element. onAttach will not
- // do this for us, because it is only called when the widget itself is
- // attached to the document.
- DOM.setEventListener(elem, this);
- }
- }
+ super.setElement(elem);
+ if (attached) {
+ // Hook the event listener back up on the new element. onAttach will not
+ // do this for us, because it is only called when the widget itself is
+ // attached to the document.
+ DOM.setEventListener(elem, this);
+ }
+ }
/**
* Gets the panel-defined layout data associated with this widget.
@@ -172,10 +176,12 @@
* @param parent the widget's new parent
*/
void setParent(Widget parent) {
+ Widget oldParent = this.parent;
this.parent = parent;
-
if (parent == null) {
- onDetach();
+ if (oldParent != null && oldParent.isAttached()) {
+ onDetach();
+ }
} else if (parent.isAttached()) {
onAttach();
}