Author: [email protected]
Date: Fri Mar  6 07:55:47 2009
New Revision: 4948

Added:
    releases/1.6/user/test/com/google/gwt/user/client/ui/RootPanelTest.java
Modified:
    releases/1.6/user/src/com/google/gwt/i18n/client/BidiUtils.java
    releases/1.6/user/src/com/google/gwt/user/client/DOM.java
    releases/1.6/user/src/com/google/gwt/user/client/Event.java
    releases/1.6/user/src/com/google/gwt/user/client/impl/DOMImpl.java
    releases/1.6/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
    releases/1.6/user/src/com/google/gwt/user/client/ui/RootPanel.java
    releases/1.6/user/src/com/google/gwt/user/client/ui/SuggestBox.java
    releases/1.6/user/src/com/google/gwt/user/client/ui/Widget.java
    releases/1.6/user/test/com/google/gwt/user/UISuite.java
     
releases/1.6/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
    releases/1.6/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
    releases/1.6/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java

Log:
This patch fixes the following issues:
- issue 1937: RootPanel no longer inappropriately caches the results of
   getElementById().
- issue 3040: SuggestBox gets a wrap() method.
- HTMLPanel now cleans up its hiddenDiv immediately after using it (c.f. the
   long discussion attached to issue 1937).
- General wrapping cleanup and simplification (mostly needed for
   SuggestBox.wrap(), but also generally useful):
   - It is now possible to wrap() an Element in the DOM and move the wrapped
     widget to another parent, including a composite.
     - RootPanel no longer throws an assertion error if it discovers a  
widget in
       the detach list on unload. Rather, it now asserts in
       detachOnWindowClose() to see whether the element being wrapped has a
       parent widget anywhere in its hierarchy. This throws an exception  
closer
       to the site of the error.
Patch by: jgw
Review by: jlabanca


Modified: releases/1.6/user/src/com/google/gwt/i18n/client/BidiUtils.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/i18n/client/BidiUtils.java      
(original)
+++ releases/1.6/user/src/com/google/gwt/i18n/client/BidiUtils.java     Fri  
Mar  6 07:55:47 2009
@@ -15,8 +15,7 @@
   */
  package com.google.gwt.i18n.client;

-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.DOM;
+import com.google.gwt.dom.client.Element;
  import com.google.gwt.i18n.client.HasDirection.Direction;

  /**
@@ -50,8 +49,7 @@
     *         <code>DEFAULT</code> if the directionality is not explicitly  
set
     */
    public static HasDirection.Direction getDirectionOnElement(Element elem)  
{
-
-    String dirPropertyValue = DOM.getElementProperty(elem,  
DIR_PROPERTY_NAME);
+    String dirPropertyValue = elem.getPropertyString(DIR_PROPERTY_NAME);

      if (DIR_PROPERTY_VALUE_RTL.equalsIgnoreCase(dirPropertyValue)) {
        return HasDirection.Direction.RTL;
@@ -71,15 +69,14 @@
     *                  <code>DEFAULT</code> if the directionality should be  
removed from the element
     */
    public static void setDirectionOnElement(Element elem, Direction  
direction) {
-
      switch (direction) {
        case RTL: {
-        DOM.setElementProperty(elem, DIR_PROPERTY_NAME,  
DIR_PROPERTY_VALUE_RTL);
+        elem.setPropertyString(DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_RTL);
          break;
        }

        case LTR: {
-        DOM.setElementProperty(elem, DIR_PROPERTY_NAME,  
DIR_PROPERTY_VALUE_LTR);
+        elem.setPropertyString(DIR_PROPERTY_NAME, DIR_PROPERTY_VALUE_LTR);
          break;
        }

@@ -87,7 +84,7 @@
          if (getDirectionOnElement(elem) != HasDirection.Direction.DEFAULT)  
{
            // only clear out the the dir property if it has already been  
set to something
            // explicitly
-          DOM.setElementProperty(elem, DIR_PROPERTY_NAME, "");
+          elem.setPropertyString(DIR_PROPERTY_NAME, "");
          }
          break;
        }

Modified: releases/1.6/user/src/com/google/gwt/user/client/DOM.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/DOM.java   (original)
+++ releases/1.6/user/src/com/google/gwt/user/client/DOM.java   Fri Mar  6  
07:55:47 2009
@@ -784,6 +784,17 @@
    }

    /**
+   * Gets the {...@link EventListener} that will receive events for the given
+   * element. Only one such listener may exist for a single element.
+   *
+   * @param elem the element whose listener is to be set
+   * @return the element's event listener
+   */
+  public static EventListener getEventListener(Element elem) {
+    return impl.getEventListener(elem);
+  }
+
+  /**
     * Gets the current set of events sunk by a given element.
     *
     * @param elem the element whose events are to be retrieved

Modified: releases/1.6/user/src/com/google/gwt/user/client/Event.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/Event.java (original)
+++ releases/1.6/user/src/com/google/gwt/user/client/Event.java Fri Mar  6  
07:55:47 2009
@@ -503,6 +503,17 @@
    }

    /**
+   * Gets the {...@link EventListener} that will receive events for the given
+   * element. Only one such listener may exist for a single element.
+   *
+   * @param elem the element whose listener is to be set
+   * @return the element's event listener
+   */
+  public static EventListener getEventListener(Element elem) {
+    return DOM.getEventListener((com.google.gwt.user.client.Element) elem);
+  }
+
+  /**
     * Sets the {...@link EventListener} to receive events for the given  
element.
     * Only one such listener may exist for a single element.
     *

Modified: releases/1.6/user/src/com/google/gwt/user/client/impl/DOMImpl.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/impl/DOMImpl.java   
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/impl/DOMImpl.java  Fri  
Mar  6 07:55:47 2009
@@ -100,6 +100,10 @@

    public abstract int getChildIndex(Element parent, Element child);

+  public native EventListener getEventListener(Element elem) /*-{
+    return elem.__listener;
+  }-*/;
+
    public native int getEventsSunk(Element elem) /*-{
      return elem.__eventBits || 0;
    }-*/;

Modified: releases/1.6/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/ui/HTMLPanel.java   
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/ui/HTMLPanel.java  Fri  
Mar  6 07:55:47 2009
@@ -116,7 +116,7 @@
      if (hiddenDiv == null) {
        hiddenDiv = DOM.createDiv();
        UIObject.setVisible(hiddenDiv, false);
-      DOM.appendChild(RootPanel.getBodyElement(), hiddenDiv);
+      RootPanel.getBodyElement().appendChild(hiddenDiv);
      }

      // Hang on to the panel's original parent and sibling elements so that  
it
@@ -133,6 +133,8 @@
      // Put the panel's element back where it was.
      if (origParent != null) {
        DOM.insertBefore(origParent, getElement(), origSibling);
+    } else {
+      DOM.removeChild(hiddenDiv, getElement());
      }

      return child;

Modified: releases/1.6/user/src/com/google/gwt/user/client/ui/RootPanel.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/ui/RootPanel.java   
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/ui/RootPanel.java  Fri  
Mar  6 07:55:47 2009
@@ -15,14 +15,16 @@
   */
  package com.google.gwt.user.client.ui;

+import com.google.gwt.dom.client.BodyElement;
  import com.google.gwt.dom.client.Document;
  import com.google.gwt.event.logical.shared.CloseEvent;
  import com.google.gwt.event.logical.shared.CloseHandler;
  import com.google.gwt.i18n.client.BidiUtils;
  import com.google.gwt.i18n.client.HasDirection;
  import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.EventListener;
  import com.google.gwt.user.client.Window;

  import java.util.HashMap;
@@ -32,7 +34,8 @@

  /**
   * The panel to which all other widgets must ultimately be added.  
RootPanels are
- * never created directly. Rather, they are accessed via {...@link  
RootPanel#get()}.
+ * never created directly. Rather, they are accessed via {...@link  
RootPanel#get()}
+ * .
   *
   * <p>
   * Most applications will add widgets to the default root panel in their
@@ -67,24 +70,24 @@
    /**
     * Marks a widget as detached and removes it from the detach list.
     *
+   * <p>
     * If an element belonging to a widget originally passed to
-   * {...@link #detachOnWindowClose(Widget)} has been removed from the  
document, calling
-   * this method will cause it to be marked as detached immediately.  
Failure to
-   * do so will keep the widget from being garbage collected until the  
page is
-   * unloaded.
+   * {...@link #detachOnWindowClose(Widget)} has been removed from the  
document,
+   * calling this method will cause it to be marked as detached  
immediately.
+   * Failure to do so will keep the widget from being garbage collected  
until
+   * the page is unloaded.
+   * </p>
     *
+   * <p>
     * This method may only be called per widget, and only for widgets that  
were
-   * originally passed to {...@link #detachOnWindowClose(Widget)}. Any widget  
in the
-   * detach list, whose element is no longer in the document when the page
-   * unloads, will cause an assertion error.
+   * originally passed to {...@link #detachOnWindowClose(Widget)}.
+   * </p>
     *
     * @param widget the widget that no longer needs to be cleaned up when  
the
     *          page closes
     * @see #detachOnWindowClose(Widget)
     */
    public static void detachNow(Widget widget) {
-     
assert !getBodyElement().isOrHasChild(widget.getElement()) : "detachNow() "
-        + "called on a widget whose element is still attached to the  
document";
      assert widgetsToDetach.contains(widget) : "detachNow() called on a  
widget "
          + "not currently in the detach list";

@@ -96,12 +99,20 @@
     * Adds a widget to the detach list. This is the list of widgets to be
     * detached when the page unloads.
     *
+   * <p>
     * This method must be called for all widgets that have no parent  
widgets.
     * These are most commonly {...@link RootPanel RootPanels}, but can also be 
 
any
     * widget used to wrap an existing element on the page. Failing to do  
this may
     * cause these widgets to leak memory. This method is called  
automatically by
     * widgets' wrap methods (e.g.
     * {...@link Button#wrap(com.google.gwt.dom.client.Element)}).
+   * </p>
+   *
+   * <p>
+   * This method may <em>not</em> be called on any widget whose element is
+   * contained in another widget. This is to ensure that the DOM and Widget
+   * hierarchies cannot get into an inconsistent state.
+   * </p>
     *
     * @param widget the widget to be cleaned up when the page closes
     * @see #detachNow(Widget)
@@ -109,12 +120,14 @@
    public static void detachOnWindowClose(Widget widget) {
      assert !widgetsToDetach.contains(widget) : "detachOnUnload() called  
twice "
          + "for the same widget";
+    assert !isElementChildOfWidget(widget.getElement()) : "A widget that  
has "
+        + "an existing parent widget may not be added to the detach list";

      widgetsToDetach.add(widget);
    }

    /**
-   * Gets the default root panel. This panel wraps body of the browser's
+   * Gets the default root panel. This panel wraps the body of the  
browser's
     * document. This root panel can contain any number of widgets, which  
will be
     * laid out in their natural HTML ordering. Many applications, however,  
will
     * add a single panel to the RootPanel to provide more structure.
@@ -130,24 +143,34 @@
     * work, the HTML document into which the application is loaded must have
     * specified an element with the given id.
     *
-   * @param id the id of the element to be wrapped with a root panel
+   * @param id the id of the element to be wrapped with a root panel (
+   *          <code>null</code> specifies the default instance, which  
wraps the
+   *          &lt;body&gt; element)
     * @return the root panel, or <code>null</code> if no such element was  
found
     */
    public static RootPanel get(String id) {
      // See if this RootPanel is already created.
      RootPanel rp = rootPanels.get(id);
-    if (rp != null) {
-      return rp;
-    }

      // Find the element that this RootPanel will wrap.
      Element elem = null;
      if (id != null) {
-      if (null == (elem = DOM.getElementById(id))) {
+      // Return null if the id is specified, but no element is found.
+      if (null == (elem = Document.get().getElementById(id))) {
          return null;
        }
      }

+    if (rp != null) {
+      // If the element associated with an existing RootPanel has been  
replaced
+      // for any reason, return a new RootPanel rather than the existing  
one (
+      // see issue 1937).
+      if ((elem == null) || (rp.getElement() == elem)) {
+        // There's already an existing RootPanel for this element. Return  
it.
+        return rp;
+      }
+    }
+
      // Note that the code in this if block only happens once -
      // on the first RootPanel.get(String) or RootPanel.get()
      // call.
@@ -181,10 +204,20 @@
     *
     * @return the document's body element
     */
-  public static native Element getBodyElement() /*-{
+  public static native com.google.gwt.user.client.Element getBodyElement()  
/*-{
      return $doc.body;
    }-*/;

+  /**
+   * Determines whether the given widget is in the detach list.
+   *
+   * @param widget the widget to be checked
+   * @return <code>true</code> if the widget is in the detach list
+   */
+  public static boolean isInDetachList(Widget widget) {
+    return widgetsToDetach.contains(widget);
+  }
+
    // Package-protected for use by unit tests. Do not call this method  
directly.
    static void detachWidgets() {
      // When the window is closing, detach all widgets that need to be
@@ -194,16 +227,9 @@
        if (widget.isAttached()) {
          widget.onDetach();
        }
-
-      // Assert that each widget's element is actually attached to the
-      // document. If not, then it was probably wrapped and removed, but  
not
-      // properly detached.
-      assert getBodyElement().isOrHasChild(widget.getElement()) : "A "
-          + "widget in the detach list was found not attached to the "
-          + "document. The is likely caused by wrapping an existing "
-          + "element and removing it from the document without calling "
-          + "RootPanel.detachNow().";
      }
+
+    widgetsToDetach.clear();
    }

    /**
@@ -224,8 +250,29 @@
      });
    }

+  /*
+   * Checks to see whether the given element has any parent element that
+   * belongs to a widget. This is not terribly efficient, and is thus only  
used
+   * in an assertion.
+   */
+  private static boolean isElementChildOfWidget(Element element) {
+    // Walk up the DOM hierarchy, looking for any widget with an event  
listener
+    // set. Though it is not dependable in the general case that a widget  
will
+    // have set its element's event listener at all times, it *is*  
dependable
+    // if the widget is attached. Which it will be in this case.
+    element = element.getParentElement();
+    BodyElement body = Document.get().getBody();
+    while ((element != null) && (body != element)) {
+      if (Event.getEventListener(element) != null) {
+        return true;
+      }
+      element = element.getParentElement().cast();
+    }
+    return false;
+  }
+
    private RootPanel(Element elem) {
-    super(elem);
+    super(elem.<com.google.gwt.user.client.Element> cast());
      onAttach();
    }
  }

Modified:  
releases/1.6/user/src/com/google/gwt/user/client/ui/SuggestBox.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/ui/SuggestBox.java  
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/ui/SuggestBox.java Fri  
Mar  6 07:55:47 2009
@@ -15,6 +15,8 @@
   */
  package com.google.gwt.user.client.ui;

+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
  import com.google.gwt.event.dom.client.HandlesAllKeyEvents;
  import com.google.gwt.event.dom.client.HasAllKeyHandlers;
  import com.google.gwt.event.dom.client.KeyCodes;
@@ -207,6 +209,7 @@
      }
    }

+
    /**
     * Class for menu items in a SuggestionMenu. A SuggestionMenuItem  
differs from
     * a MenuItem in that each item is backed by a Suggestion object. The  
text of
@@ -240,6 +243,31 @@
    }

    private static final String STYLENAME_DEFAULT = "gwt-SuggestBox";
+
+  /**
+   * Creates a {...@link SuggestBox} widget that wraps an existing &lt;input
+   * type='text'&gt; element.
+   *
+   * This element must already be attached to the document. If the element  
is
+   * removed from the document, you must call
+   * {...@link RootPanel#detachNow(Widget)}.
+   *
+   * @param oracle the suggest box oracle to use
+   * @param element the element to be wrapped
+   */
+  public static SuggestBox wrap(SuggestOracle oracle, Element element) {
+    // Assert that the element is attached.
+    assert Document.get().getBody().isOrHasChild(element);
+
+    TextBox textBox = new TextBox(element);
+    SuggestBox suggestBox = new SuggestBox(oracle, textBox);
+
+    // Mark it attached and remember it for cleanup.
+    suggestBox.onAttach();
+    RootPanel.detachOnWindowClose(suggestBox);
+
+    return suggestBox;
+  }

    private int limit = 20;
    private boolean selectsFirstItem = true;

Modified: releases/1.6/user/src/com/google/gwt/user/client/ui/Widget.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/ui/Widget.java      
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/ui/Widget.java     Fri  
Mar  6 07:55:47 2009
@@ -89,16 +89,29 @@
      }
      DomEvent.fireNativeEvent(event, this);
    }
-
+
    /**
-   * Removes this widget from its parent widget. If it has no parent, this
-   * method does nothing.
+   * Removes this widget from its parent widget, if one exists.
+   *
+   * <p>
+   * If it has no parent, this method does nothing. If it is a "root"  
widget
+   * (meaning it's been added to the detach list via
+   * {...@link RootPanel#detachOnWindowClose(Widget)}), it will be removed  
from the
+   * detached immediately. This makes it possible for Composites and  
Panels to
+   * adopt root widgets.
+   * </p>
     *
     * @throws IllegalStateException if this widget's parent does not support
     *           removal (e.g. {...@link Composite})
     */
    public void removeFromParent() {
-    if (parent instanceof HasWidgets) {
+    if (parent == null) {
+      // If the widget had no parent, check to see if it was in the detach  
list
+      // and remove it if necessary.
+      if (RootPanel.isInDetachList(this)) {
+        RootPanel.detachNow(this);
+      }
+    } else if (parent instanceof HasWidgets) {
        ((HasWidgets) parent).remove(this);
      } else if (parent != null) {
        throw new IllegalStateException(

Modified: releases/1.6/user/test/com/google/gwt/user/UISuite.java
==============================================================================
--- releases/1.6/user/test/com/google/gwt/user/UISuite.java     (original)
+++ releases/1.6/user/test/com/google/gwt/user/UISuite.java     Fri Mar  6  
07:55:47 2009
@@ -15,6 +15,8 @@
   */
  package com.google.gwt.user;

+import junit.framework.Test;
+
  import com.google.gwt.junit.tools.GWTTestSuite;
  import com.google.gwt.user.client.CommandExecutorTest;
  import com.google.gwt.user.client.CookieTest;
@@ -22,7 +24,6 @@
  import com.google.gwt.user.client.WindowTest;
  import com.google.gwt.user.client.ui.AbsolutePanelTest;
  import com.google.gwt.user.client.ui.AnchorTest;
-import com.google.gwt.user.client.ui.WidgetTest;
  import com.google.gwt.user.client.ui.ButtonTest;
  import com.google.gwt.user.client.ui.CaptionPanelTest;
  import com.google.gwt.user.client.ui.CheckBoxTest;
@@ -65,6 +66,7 @@
  import com.google.gwt.user.client.ui.PrefixTreeTest;
  import com.google.gwt.user.client.ui.RadioButtonTest;
  import com.google.gwt.user.client.ui.RichTextAreaTest;
+import com.google.gwt.user.client.ui.RootPanelTest;
  import com.google.gwt.user.client.ui.ScrollPanelTest;
  import com.google.gwt.user.client.ui.SimpleCheckBoxTest;
  import com.google.gwt.user.client.ui.SimpleRadioButtonTest;
@@ -81,13 +83,12 @@
  import com.google.gwt.user.client.ui.WidgetIteratorsTest;
  import com.google.gwt.user.client.ui.WidgetOnLoadTest;
  import com.google.gwt.user.client.ui.WidgetSubclassingTest;
+import com.google.gwt.user.client.ui.WidgetTest;
  import com.google.gwt.user.client.ui.impl.ClippedImagePrototypeTest;
  import com.google.gwt.user.datepicker.client.DateChangeEventTest;
  import com.google.gwt.user.rebind.ui.ImageBundleGeneratorTest;
  import com.google.gwt.xml.client.XMLTest;

-import junit.framework.Test;
-
  /**
   * Tests of the ui package.
   */
@@ -166,6 +167,7 @@
      suite.addTestSuite(DateChangeEventTest.class);
      suite.addTestSuite(CreateEventTest.class);
      suite.addTestSuite(WidgetTest.class);
+    suite.addTestSuite(RootPanelTest.class);
      return suite;
    }
  }

Modified:  
releases/1.6/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java
==============================================================================
---  
releases/1.6/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java   
 
(original)
+++  
releases/1.6/user/test/com/google/gwt/user/client/ui/ElementWrappingTest.java   
 
Fri Mar  6 07:55:47 2009
@@ -34,6 +34,9 @@
      return "com.google.gwt.user.UserTest";
    }

+  /**
+   * Tests {...@link Anchor#wrap(Element)}.
+   */
    public void testAnchor() {
      ensureDiv().setInnerHTML("<a id='foo' href='" + TEST_URL  
+ "'>myAnchor</a>");
      Anchor anchor = Anchor.wrap(Document.get().getElementById("foo"));
@@ -43,6 +46,9 @@
      assertEquals("myAnchor", anchor.getText());
    }

+  /**
+   * Tests {...@link Button#wrap(Element)}.
+   */
    public void testButton() {
      ensureDiv().setInnerHTML("<button id='foo'>myButton</button>");
      Button button = Button.wrap(Document.get().getElementById("foo"));
@@ -51,6 +57,51 @@
      assertEquals("myButton", button.getText());
    }

+  /**
+   * Tests that {...@link RootPanel#detachNow(Widget)} can only be called  
once per
+   * widget.
+   */
+  public void testDetachNowTwiceFails() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      try {
+        // Trying to pass the same widget to RootPanel.detachNow() twice
+        // should fail an assertion.
+        ensureDiv().setInnerHTML(
+            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
+        Anchor a = Anchor.wrap(Document.get().getElementById("foo"));
+        RootPanel.detachNow(a); // pass
+        RootPanel.detachNow(a); // fail
+        fail("Expected assertion failure calling detachNow() twice");
+      } catch (AssertionError e) {
+      }
+    }
+  }
+
+  /**
+   * Tests that {...@link RootPanel#detachOnWindowClose(Widget)} can only be  
called
+   * once per widget.
+   */
+  public void testDetachOnWindowCloseTwiceFails() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      try {
+        // Trying to pass the same widget to RootPanel.detachOnUnload()  
twice
+        // should fail an assertion (the first call is implicit through
+        // Anchor.wrap()).
+        ensureDiv().setInnerHTML(
+            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
+        Anchor a = Anchor.wrap(Document.get().getElementById("foo")); //  
pass
+        RootPanel.detachOnWindowClose(a); // fail
+        fail("Expected assertion failure calling detachOnLoad() twice");
+      } catch (AssertionError e) {
+      }
+    }
+  }
+
+  /**
+   * Tests {...@link FileUpload#wrap(Element)}.
+   */
    public void testFileUpload() {
      ensureDiv().setInnerHTML("<input type='file'  
id='foo'>myInput</input>");
      FileUpload upload =  
FileUpload.wrap(Document.get().getElementById("foo"));
@@ -58,6 +109,9 @@
      assertExistsAndAttached(upload);
    }

+  /**
+   * Tests {...@link FormPanel#wrap(Element)}.
+   */
    public void testFormPanel() {
      ensureDiv().setInnerHTML("<form id='foo'></form>");
      FormPanel formPanel =  
FormPanel.wrap(Document.get().getElementById("foo"));
@@ -65,6 +119,9 @@
      assertExistsAndAttached(formPanel);
    }

+  /**
+   * Tests {...@link Frame#wrap(Element)}.
+   */
    public void testFrame() {
      ensureDiv().setInnerHTML("<iframe id='foo'>myFrame</iframe>");
      Frame frame = Frame.wrap(Document.get().getElementById("foo"));
@@ -72,6 +129,9 @@
      assertExistsAndAttached(frame);
    }

+  /**
+   * Tests {...@link Hidden#wrap(Element)}.
+   */
    public void testHidden() {
      ensureDiv().setInnerHTML("<input type='hidden' id='foo'></input>");
      Hidden hidden = Hidden.wrap(Document.get().getElementById("foo"));
@@ -79,6 +139,9 @@
      assertExistsAndAttached(hidden);
    }

+  /**
+   * Tests {...@link HTML#wrap(Element)}.
+   */
    public void testHTML() {
      ensureDiv().setInnerHTML("<div id='foo'>myHTML</div>");
      HTML html = HTML.wrap(Document.get().getElementById("foo"));
@@ -87,6 +150,9 @@
      assertEquals("myHTML", html.getHTML());
    }

+  /**
+   * Tests {...@link Image#wrap(Element)}.
+   */
    public void testImage() {
      ensureDiv().setInnerHTML("<img id='foo' src='" + IMG_URL + "'>");
      Image image = Image.wrap(Document.get().getElementById("foo"));
@@ -95,6 +161,9 @@
      assertEquals(IMG_URL, image.getUrl());
    }

+  /**
+   * Tests {...@link InlineHTML#wrap(Element)}.
+   */
    public void testInlineHTML() {
      ensureDiv().setInnerHTML("<span id='foo'>myInlineHTML</span>");
      InlineHTML html =  
InlineHTML.wrap(Document.get().getElementById("foo"));
@@ -103,6 +172,9 @@
      assertEquals("myInlineHTML", html.getHTML());
    }

+  /**
+   * Tests {...@link InlineLabel#wrap(Element)}.
+   */
    public void testInlineLabel() {
      ensureDiv().setInnerHTML("<span id='foo'>myInlineLabel</span>");
      InlineLabel label =  
InlineLabel.wrap(Document.get().getElementById("foo"));
@@ -111,6 +183,9 @@
      assertEquals("myInlineLabel", label.getText());
    }

+  /**
+   * Tests {...@link Label#wrap(Element)}.
+   */
    public void testLabel() {
      ensureDiv().setInnerHTML("<div id='foo'>myLabel</div>");
      Label label = Label.wrap(Document.get().getElementById("foo"));
@@ -119,6 +194,9 @@
      assertEquals("myLabel", label.getText());
    }

+  /**
+   * Tests {...@link ListBox#wrap(Element)}.
+   */
    public void testListBox() {
      ensureDiv().setInnerHTML("<select id='foo'></select>");
      ListBox listBox = ListBox.wrap(Document.get().getElementById("foo"));
@@ -126,6 +204,41 @@
      assertExistsAndAttached(listBox);
    }

+  /**
+   * Tests that all widgets passed to
+   * {...@link RootPanel#detachOnWindowClose(Widget)} are cleaned up properly  
when
+   * the window is unloaded, regardless of whether their associated  
elements are
+   * still in the DOM or not.
+   */
+  public void testOnUnloadDetachesAllWidgets() {
+    // Testing hosted-mode-only assertion.
+    if (!GWT.isScript()) {
+      ensureDiv().setInnerHTML(
+          "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>"
+              + "<a id='bar' href='" + TEST_URL + "'>myOtherAnchor</a>");
+
+      // Wrap one widget that will be left in the DOM normally.
+      Element fooElem = Document.get().getElementById("foo");
+      Anchor fooAnchor = Anchor.wrap(fooElem);
+
+      // Wrap another widget and remove its element from the DOM.
+      Element barElem = Document.get().getElementById("bar");
+      Anchor barAnchor = Anchor.wrap(barElem);
+      barElem.getParentElement().removeChild(barElem);
+
+      // Fake an unload by telling the RootPanel to go ahead and detach all
+      // of its widgets.
+      RootPanel.detachWidgets();
+
+      // Now make sure that both widgets were detached properly.
+      assertFalse("fooAnchor should have been detached",  
fooAnchor.isAttached());
+      assertFalse("barAnchor should have been detached",  
barAnchor.isAttached());
+    }
+  }
+
+  /**
+   * Tests {...@link PasswordTextBox#wrap(Element)}.
+   */
    public void testPasswordTextBox() {
      ensureDiv().setInnerHTML("<input type='password' id='foo'></input>");
      PasswordTextBox textBox =  
PasswordTextBox.wrap(Document.get().getElementById(
@@ -134,6 +247,9 @@
      assertExistsAndAttached(textBox);
    }

+  /**
+   * Tests {...@link SimpleCheckBox#wrap(Element)}.
+   */
    public void testSimpleCheckBox() {
      ensureDiv().setInnerHTML("<input type='checkbox' id='foo'></input>");
      SimpleCheckBox checkBox =  
SimpleCheckBox.wrap(Document.get().getElementById(
@@ -142,6 +258,9 @@
      assertExistsAndAttached(checkBox);
    }

+  /**
+   * Tests {...@link SimpleRadioButton#wrap(Element)}.
+   */
    public void testSimpleRadioButton() {
      ensureDiv().setInnerHTML("<input type='radio' id='foo'></input>");
      SimpleRadioButton radio =  
SimpleRadioButton.wrap(Document.get().getElementById(
@@ -150,6 +269,9 @@
      assertExistsAndAttached(radio);
    }

+  /**
+   * Tests {...@link TextArea#wrap(Element)}.
+   */
    public void testTextArea() {
      ensureDiv().setInnerHTML("<textarea rows='1' cols='1'  
id='foo'></textarea>");
      TextArea textArea =  
TextArea.wrap(Document.get().getElementById("foo"));
@@ -157,6 +279,9 @@
      assertExistsAndAttached(textArea);
    }

+  /**
+   * Tests {...@link TextBox#wrap(Element)}.
+   */
    public void testTextBox() {
      ensureDiv().setInnerHTML("<input type='text' id='foo'></input>");
      TextBox textBox = TextBox.wrap(Document.get().getElementById("foo"));
@@ -164,40 +289,34 @@
      assertExistsAndAttached(textBox);
    }

-  public void testDetachOnUnloadTwiceFails() {
+  /**
+   * Tests that wrapping an element that is already a child of an existing
+   * widget's element fails.
+   */
+  public void testWrappingChildElementFails() {
      // Testing hosted-mode-only assertion.
      if (!GWT.isScript()) {
        try {
-        // Trying to pass the same widget to RootPanel.detachOnUnload()  
twice
-        // should fail an assertion (the first call is implicit through
-        // Anchor.wrap()).
-        ensureDiv().setInnerHTML(
-            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
-        Anchor a = Anchor.wrap(Document.get().getElementById("foo")); //  
pass
-        RootPanel.detachOnWindowClose(a); // fail
-        fail("Expected assertion failure calling detachOnLoad() twice");
-      } catch (AssertionError e) {
-      }
-    }
-  }
-
-  public void testDetachNowTwiceFails() {
-    // Testing hosted-mode-only assertion.
-    if (!GWT.isScript()) {
-      try {
-        // Trying to pass the same widget to RootPanel.detachNow() twice
-        // should fail an assertion.
-        ensureDiv().setInnerHTML(
-            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
-        Anchor a = Anchor.wrap(Document.get().getElementById("foo"));
-        RootPanel.detachNow(a); // pass
-        RootPanel.detachNow(a); // fail
-        fail("Expected assertion failure calling detachNow() twice");
+        // Create a panel that contains HTML with a unique id, which we're
+        // going to try and wrap below.
+        FlowPanel p = new FlowPanel();
+        RootPanel.get().add(p);
+        p.add(new HTML("<a id='twcef_id'>foo</a>"));
+
+        // Get the element and try to wrap it.
+        Element unwrappableElement =  
Document.get().getElementById("twcef_id");
+        Anchor.wrap(unwrappableElement);
+        fail("Attempting to wrap the above element should have failed.");
        } catch (AssertionError e) {
+        // Expected error.
        }
      }
    }

+  /**
+   * Tests that wrap() may only be called on elements that are already  
attached
+   * to the DOM.
+   */
    public void testWrapUnattachedFails() {
      // Testing hosted-mode-only assertion.
      if (!GWT.isScript()) {
@@ -209,28 +328,6 @@
          AnchorElement aElem = Document.get().createAnchorElement();
          Anchor.wrap(aElem);
          fail("Expected assertion failure wrapping unattached element");
-      } catch (AssertionError e) {
-      }
-    }
-  }
-
-  public void testOnUnloadAssertions() {
-    // Testing hosted-mode-only assertion.
-    if (!GWT.isScript()) {
-      try {
-        // When a wrap()ed element is detached from the document without  
being
-        // properly unwrapped, there will be an assertion to catch this  
run on
-        // unload.
-        ensureDiv().setInnerHTML(
-            "<a id='foo' href='" + TEST_URL + "'>myAnchor</a>");
-        Element aElem = Document.get().getElementById("foo");
-        Anchor.wrap(aElem);
-        aElem.getParentElement().removeChild(aElem);
-
-        // Fake an unload by telling the RootPanel to go ahead and detach  
all
-        // of its widgets.
-        RootPanel.detachWidgets();
-        fail("Assertion expected for orphaned wrap()ed widgets");
        } catch (AssertionError e) {
        }
      }

Modified:  
releases/1.6/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
==============================================================================
--- releases/1.6/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java     
 
(original)
+++ releases/1.6/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java     
 
Fri Mar  6 07:55:47 2009
@@ -15,10 +15,9 @@
   */
  package com.google.gwt.user.client.ui;

+import com.google.gwt.dom.client.Element;
  import com.google.gwt.dom.client.Node;
  import com.google.gwt.junit.client.GWTTestCase;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;

  /**
   * Tests the HTMLPanel widget.
@@ -44,10 +43,8 @@
      p.add(labelA, "a");
      p.add(labelB, "b");
      // Ensure that both Label's have the correct parent.
-    assertEquals("a", DOM.getElementAttribute(
-        DOM.getParent(labelA.getElement()), "id"));
-    assertEquals("b", DOM.getElementAttribute(
-        DOM.getParent(labelB.getElement()), "id"));
+    assertEquals("a", labelA.getElement().getParentElement().getId());
+    assertEquals("b", labelB.getElement().getParentElement().getId());
    }

    /**
@@ -70,9 +67,12 @@

      // 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()));
+    assertTrue(sp.getElement().isOrHasChild(p.getElement()));
    }

+  /**
+   * Tests child attachment order using {...@link HasWidgetsTester}.
+   */
    public void testAttachDetachOrder() {
      HTMLPanel p = new HTMLPanel("<div id='w00t'></div>");
      HasWidgetsTester.testAll(p, new Adder());
@@ -96,9 +96,9 @@

      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());
+    assertTrue(fp.getElement().isOrHasChild(hp.getElement()));
+    assertTrue(ba.getElement().getNextSibling() == hp.getElement());
+    assertTrue(hp.getElement().getNextSibling() == bb.getElement());
    }

    /**
@@ -119,6 +119,10 @@
      assertEquals("bar", next.getNodeValue());
    }

+  /**
+   * Ensure that {...@link HTMLPanel#getElementById(String)} behaves properly  
in
+   * both attached and unattached states.
+   */
    public void testGetElementById() {
      HTMLPanel hp = new HTMLPanel("foo<div id='toFind'>bar</div>baz");

@@ -129,5 +133,28 @@

      // Make sure we got the same element in both cases.
      assertEquals(elem0, elem1);
+  }
+
+  /**
+   * Tests that the HTMLPanel's element is not moved from its original  
location
+   * when {...@link HTMLPanel#add(Widget, String)} is called on it while it is
+   * unattached.
+   */
+  public void testElementIsUnmoved() {
+    HTMLPanel unattached = new HTMLPanel("<div id='unattached'></div>");
+    HTMLPanel attached = new HTMLPanel("<div id='attached'></div>");
+
+    RootPanel.get().add(attached);
+
+    Element unattachedParentElem =  
unattached.getElement().getParentElement();
+    Element attachedParentElem = attached.getElement().getParentElement();
+
+    unattached.add(new Button("unattached"), "unattached");
+    attached.add(new Button("attached"), "attached");
+
+    assertEquals("Unattached's parent element should be unaffected",
+        unattachedParentElem, unattached.getElement().getParentElement());
+    assertEquals("Unattached's parent element should be unaffected",
+        attachedParentElem, attached.getElement().getParentElement());
    }
  }

Added:  
releases/1.6/user/test/com/google/gwt/user/client/ui/RootPanelTest.java
==============================================================================
--- (empty file)
+++ releases/1.6/user/test/com/google/gwt/user/client/ui/RootPanelTest.java     
 
Fri Mar  6 07:55:47 2009
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 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.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests {...@link RootPanel}.
+ */
+public class RootPanelTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  /**
+   * Ensures that {...@link RootPanel#get(String)} behaves properly.
+   */
+  public void testGetById() {
+    Document doc = Document.get();
+    DivElement div = doc.createDivElement();
+    doc.getBody().appendChild(div);
+    div.setInnerHTML("<div id='a'></div><div id='b'></div>");
+
+    // You should get the same RootPanel for subsequent calls to get()  
with the
+    // same id. But you should get *different* RootPanels for calls with
+    // different ids.
+    RootPanel aRoot = RootPanel.get("a");
+    RootPanel bRoot = RootPanel.get("b");
+
+    assertSame(
+        "RootPanel.get() should return the same instancefor the same id",
+        aRoot, RootPanel.get("a"));
+    assertSame(
+        "RootPanel.get() should return the same instancefor the same id",
+        bRoot, RootPanel.get("b"));
+    assertNotSame("RootPanels a and b should be different", aRoot, bRoot);
+
+    // If a RootPanel's element is replaced in the DOM, you should get a
+    // new RootPanel instance if you ask for it again (see issue 1937).
+    Element aElem = doc.getElementById("a");
+    Element newAElem = doc.createDivElement();
+    newAElem.setId("a");
+    aElem.getParentElement().replaceChild(newAElem, aElem);
+
+    RootPanel newARoot = RootPanel.get("a");
+    assertNotSame("New RootPanel should not be same as old", newARoot,  
aRoot);
+  }
+}

Modified:  
releases/1.6/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
==============================================================================
---  
releases/1.6/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java        
 
(original)
+++  
releases/1.6/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java        
 
Fri Mar  6 07:55:47 2009
@@ -15,6 +15,8 @@
   */
  package com.google.gwt.user.client.ui;

+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
  import com.google.gwt.junit.client.GWTTestCase;

  import java.util.Arrays;
@@ -110,7 +112,31 @@
      RootPanel.get().clear();
    }

+  public void testWrapUsingStaticWrapMethod() {
+    Element wrapper = Document.get().createTextInputElement();
+    RootPanel.get().getElement().appendChild(wrapper);
+
+    // Use direct wrap method from suggest box.
+    SuggestBox box = SuggestBox.wrap(createOracle(), wrapper);
+    assertTrue(box.isAttached());
+    assertTrue(box.getWidget().getParent() == box);
+  }
+
+  public void testWrapUsingComposite() {
+    // Ensure we can use this with normal composites
+    Element wrapper = Document.get().createTextInputElement();
+    RootPanel.get().getElement().appendChild(wrapper);
+    TextBox b = TextBox.wrap(wrapper);
+    SuggestBox box = new SuggestBox(createOracle(), b);
+    assertTrue(b.getParent() == box);
+  }
+
    protected SuggestBox createSuggestBox() {
+    MultiWordSuggestOracle oracle = createOracle();
+    return new SuggestBox(oracle);
+  }
+
+  private MultiWordSuggestOracle createOracle() {
      MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
      oracle.add("test");
      oracle.add("test1");
@@ -118,6 +144,6 @@
      oracle.add("test3");
      oracle.add("test4");
      oracle.add("john");
-    return new SuggestBox(oracle);
+    return oracle;
    }
  }

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to