Cherrypick merges of 1.6 c4298 and c4299.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4314 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3186.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3186.java
new file mode 100644
index 0000000..a8360ca
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3186.java
@@ -0,0 +1,211 @@
+/*

+ * 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.museum.client.defaultmuseum;

+

+import com.google.gwt.dom.client.Element;

+import com.google.gwt.museum.client.common.AbstractIssue;

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

+import com.google.gwt.user.client.ui.AbsolutePanel;

+import com.google.gwt.user.client.ui.FlexTable;

+import com.google.gwt.user.client.ui.FocusPanel;

+import com.google.gwt.user.client.ui.MouseListener;

+import com.google.gwt.user.client.ui.MouseListenerCollection;

+import com.google.gwt.user.client.ui.Widget;

+

+import java.util.HashMap;

+import java.util.Map;

+

+/**

+ * Ensure the ListenerWrapper for mouse still works like the old listeners did.

+ */

+@SuppressWarnings("deprecation")

+public class Issue3186 extends AbstractIssue {

+

+  enum VisibleEvents {

+    mouseDown, mouseEnter, mouseLeave, mouseMove, mouseUp

+  }

+

+  private final class TestWidget extends FocusPanel {

+    private class Control implements MouseListener {

+      int controlX;

+      int controlY;

+      int controlMouseEnter;

+      int controlMouseLeave;

+

+      public void onMouseDown(Widget sender, int x, int y) {

+        this.controlX = x;

+        this.controlY = y;

+      }

+

+      public void onMouseEnter(Widget sender) {

+        ++controlMouseEnter;

+      }

+

+      public void onMouseLeave(Widget sender) {

+        ++controlMouseLeave;

+      }

+

+      public void onMouseMove(Widget sender, int x, int y) {

+        this.controlX = x;

+        this.controlY = y;

+      }

+

+      public void onMouseUp(Widget sender, int x, int y) {

+        this.controlX = x;

+        this.controlY = y;

+      }

+    }

+

+    private class Current implements MouseListener {

+      private int mouseEnterCount;

+      private int mouseLeaveCount;

+

+      public void onMouseDown(Widget sender, int x, int y) {

+        check(x, y, VisibleEvents.mouseDown);

+      }

+

+      public void onMouseEnter(Widget sender) {

+        ++mouseEnterCount;

+        if (mouseEnterCount != control.controlMouseEnter) {

+          fail("recieved:" + mouseEnterCount + " events, expected:"

+              + control.controlMouseEnter, VisibleEvents.mouseEnter);

+        } else {

+          pass(VisibleEvents.mouseEnter);

+        }

+        sender.getElement().getStyle().setProperty("background", "yellow");

+      }

+

+      public void onMouseLeave(Widget sender) {

+        ++mouseLeaveCount;

+        if (mouseLeaveCount != control.controlMouseLeave) {

+          fail("recieved:" + mouseLeaveCount + " events, expected:"

+              + control.controlMouseLeave, VisibleEvents.mouseLeave);

+        } else {

+          pass(VisibleEvents.mouseLeave);

+        }

+

+        sender.getElement().getStyle().setProperty("background", "");

+      }

+

+      public void onMouseMove(Widget sender, int x, int y) {

+        check(x, y, VisibleEvents.mouseMove);

+      }

+

+      public void onMouseUp(Widget sender, int x, int y) {

+        check(x, y, VisibleEvents.mouseUp);

+      }

+

+      private void check(int x, int y, VisibleEvents event) {

+        String errorReport = getErrorReport(x, y);

+        if (errorReport == null) {

+          eventToElement.get(event).setInnerHTML(

+              "<span style='color:green'>pass</span>");

+        } else {

+          fail(errorReport, event);

+        }

+      }

+

+      private String getErrorReport(int x, int y) {

+        String errorReport = null;

+        if (x != control.controlX) {

+          errorReport = "wanted x: " + control.controlX + " actual x" + x;

+        } else if (y != control.controlY) {

+          errorReport += "wanted y: " + control.controlY + " actual y" + y;

+        }

+        return errorReport;

+      }

+    }

+

+    private FlexTable layout = null;

+    private MouseListenerCollection collection = new MouseListenerCollection();

+

+    private Control control = new Control();

+    private Current current = new Current();

+    private final Map<VisibleEvents, Element> eventToElement = new HashMap<VisibleEvents, Element>();

+

+    public TestWidget() {

+      layout = new FlexTable();

+      layout.setCellPadding(3);

+      layout.setBorderWidth(2);

+

+      layout.setHTML(0, 0, "<b>MouseEvents</b>");

+      layout.setHTML(0, 1, "<b>Status</b>");

+

+      for (VisibleEvents e : VisibleEvents.values()) {

+        eventToElement.put(e, addResultRow(e.name()));

+      }

+      add(layout);

+      this.addMouseListener(current);

+      collection.add(control);

+    }

+

+    public void fail(String errorReport, VisibleEvents event) {

+      eventToElement.get(event).setInnerHTML(

+          "<span style='color:red'>" + errorReport + "</span>");

+    }

+

+    @Override

+    public void onBrowserEvent(Event event) {

+      collection.fireMouseEvent(this, event);

+      super.onBrowserEvent(event);

+    }

+

+    public void pass(VisibleEvents event) {

+      eventToElement.get(event).setInnerHTML(

+          "<span style='color:green'>pass</span>");

+    }

+

+    private Element addResultRow(String eventName) {

+      int row = layout.getRowCount();

+      layout.setHTML(row, 0, eventName);

+      layout.setHTML(row, 1, "<span style='color:red'>?</span>");

+      Element cell = layout.getCellFormatter().getElement(row, 1);

+      return cell;

+    }

+  }

+

+  @Override

+  public Widget createIssue() {

+    AbsolutePanel p = new AbsolutePanel();

+    p.setHeight("500px");

+    p.setWidth("500px");

+    final TestWidget dialog = showTestWidget();

+    p.add(dialog, 100, 100);

+    return p;

+  }

+

+  @Override

+  public String getInstructions() {

+    return "move your mouse around ";

+  }

+

+  @Override

+  public String getSummary() {

+    return "mouse listeners work the same";

+  }

+

+  @Override

+  public boolean hasCSS() {

+    return false;

+  }

+

+  private TestWidget showTestWidget() {

+    final TestWidget dialog = new TestWidget();

+    return dialog;

+  }

+

+}

diff --git a/user/src/com/google/gwt/user/client/ListenerWrapper.java b/user/src/com/google/gwt/user/client/ListenerWrapper.java
index 45bf6ac..bcd0115 100644
--- a/user/src/com/google/gwt/user/client/ListenerWrapper.java
+++ b/user/src/com/google/gwt/user/client/ListenerWrapper.java
@@ -131,24 +131,29 @@
     }
   }
 
+
   // This is an internal helper method with the current formulation, we have
   // lost the info needed to make it safe by this point.
   @SuppressWarnings("unchecked")
-  protected static void baseRemove(HandlerManager manager,
+  // This is a direct copy of the baseRemove from
+  // com.google.gwt.user.client.ui.ListenerWrapper. Change in parallel.
+  static <H extends EventHandler> void baseRemove(HandlerManager manager,
       EventListener listener, Type... keys) {
     if (manager != null) {
-      for (Type key : keys) {
+      for (Type<H> key : keys) {
         int handlerCount = manager.getHandlerCount(key);
-        for (int i = 0; i < handlerCount; i++) {
-          EventHandler handler = manager.getHandler(key, i);
-          if (handler instanceof ListenerWrapper && ((ListenerWrapper) handler).listener.equals(listener)) {
+        // We are removing things as we traverse, have to go backward
+        for (int i = handlerCount - 1; i >= 0; i--) {
+          H handler = manager.getHandler(key, i);
+          if (handler instanceof ListenerWrapper
+              && ((ListenerWrapper) handler).listener.equals(listener)) {
             manager.removeHandler(key, handler);
           }
         }
       }
     }
   }
-
+  
   /**
    * Listener being wrapped.
    */
diff --git a/user/src/com/google/gwt/user/client/Window.java b/user/src/com/google/gwt/user/client/Window.java
index 50b2fc4..dae2416 100644
--- a/user/src/com/google/gwt/user/client/Window.java
+++ b/user/src/com/google/gwt/user/client/Window.java
@@ -392,12 +392,13 @@
     }
   }
 
+  // Package protected for testing.
+  static WindowHandlers handlers;
   private static boolean closeHandlersInitialized;
   private static boolean scrollHandlersInitialized;
   private static boolean resizeHandlersInitialized;
   private static final WindowImpl impl = GWT.create(WindowImpl.class);
 
-  private static WindowHandlers handlers;
 
   /**
    * Adds a {@link CloseEvent} handler.
diff --git a/user/src/com/google/gwt/user/client/ui/ListenerWrapper.java b/user/src/com/google/gwt/user/client/ui/ListenerWrapper.java
index b01d35f..bdf3024 100644
--- a/user/src/com/google/gwt/user/client/ui/ListenerWrapper.java
+++ b/user/src/com/google/gwt/user/client/ui/ListenerWrapper.java
@@ -271,25 +271,44 @@
     }

 

     public void onMouseDown(MouseDownEvent event) {

-      listener.onMouseDown(source(event), event.getClientX(),

-          event.getClientY());

+      Widget source = source(event);

+      Element elem = source.getElement();

+      listener.onMouseDown(source, event.getRelativeX(elem),

+          event.getRelativeY(elem));

     }

 

     public void onMouseMove(MouseMoveEvent event) {

-      listener.onMouseMove(source(event), event.getClientX(),

-          event.getClientY());

+      Widget source = source(event);

+      Element elem = source.getElement();

+      listener.onMouseMove(source, event.getRelativeX(elem),

+          event.getRelativeY(elem));

     }

 

     public void onMouseOut(MouseOutEvent event) {

-      listener.onMouseLeave(source(event));

+      // Only fire the mouseLeave event if it's actually leaving this

+      // widget.

+      Element to = event.getToElement();

+      Widget source = source(event);

+      if (to == null || !source.getElement().isOrHasChild(to)) {

+        listener.onMouseLeave(source(event));

+      }

     }

 

     public void onMouseOver(MouseOverEvent event) {

-      listener.onMouseEnter(source(event));

+      // Only fire the mouseEnter event if it's coming from outside this

+      // widget.

+      Element from = event.getFromElement();

+      Widget source = source(event);

+      if (from == null || !source.getElement().isOrHasChild(from)) {

+        listener.onMouseEnter(source(event));

+      }

     }

 

     public void onMouseUp(MouseUpEvent event) {

-      listener.onMouseUp(source(event), event.getClientX(), event.getClientY());

+      Widget source = source(event);

+      Element elem = source.getElement();

+      listener.onMouseUp(source, event.getRelativeX(elem),

+          event.getRelativeY(elem));

     }

   }

   public static class MouseWheel extends ListenerWrapper<MouseWheelListener> implements

@@ -510,6 +529,8 @@
       EventListener listener, Type... keys) {

     HandlerManager manager = eventSource.getHandlers();

     if (manager != null) {

+      // This is a direct copy of the baseRemove from

+      // com.google.gwt.user.client.ListenerWrapper. Change in parallel.

       for (Type<H> key : keys) {

         int handlerCount = manager.getHandlerCount(key);

         // We are removing things as we traverse, have to go backward

diff --git a/user/test/com/google/gwt/user/client/WindowTest.java b/user/test/com/google/gwt/user/client/WindowTest.java
index 04e2302..b0e0ea6 100644
--- a/user/test/com/google/gwt/user/client/WindowTest.java
+++ b/user/test/com/google/gwt/user/client/WindowTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client;
 
+import com.google.gwt.event.logical.shared.ResizeEvent;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
@@ -176,4 +177,40 @@
     // Cleanup the window
     RootPanel.get().remove(largeDOM);
   }
+
+  @SuppressWarnings("deprecation")
+  static class ListenerTester implements WindowResizeListener {
+    static int resize = 0;
+ 
+    public void onWindowResized(int width, int height) {
+      ++resize;
+    }
+
+    public static void fire() {
+      resize = 0;
+      ResizeEvent.fire(Window.handlers, 0, 0);
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public void testListenerRemoval() {
+
+    WindowResizeListener r1 = new ListenerTester();
+    WindowResizeListener r2 = new ListenerTester();
+
+    Window.addWindowResizeListener(r1);
+    Window.addWindowResizeListener(r2);
+
+    ListenerTester.fire();
+    assertEquals(ListenerTester.resize, 2);
+
+    Window.removeWindowResizeListener(r1);
+    ListenerTester.fire();
+    assertEquals(ListenerTester.resize, 1);
+    
+    Window.removeWindowResizeListener(r2);
+    ListenerTester.fire();
+    assertEquals(ListenerTester.resize, 0);
+  }
+
 }
diff --git a/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java b/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
index 5bb0f60..c7ba8e2 100644
--- a/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/CheckBoxTest.java
@@ -15,8 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerManager;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -29,7 +31,7 @@
   @Override
   public String getModuleName() {
     return "com.google.gwt.user.DebugTest";
-  }  
+  }
 
   @Override
   protected void gwtSetUp() throws Exception {
@@ -85,7 +87,7 @@
     UIObjectTest.assertDebugId("myCheck-input", newInput);
     UIObjectTest.assertDebugIdContents("myCheck-label", "myLabel");
   }
-  
+
   public void testValueChangeEvent() {
     CheckBox cb = new CheckBox();
     Handler h = new Handler();
@@ -94,7 +96,7 @@
     assertNull(h.received);
     cb.setChecked(true);
     assertNull(h.received);
-    
+
     cb.setValue(false);
     assertNull(h.received);
     cb.setValue(true);
@@ -102,17 +104,61 @@
 
     cb.setValue(true, true);
     assertNull(h.received);
-    
+
     cb.setValue(false, true);
     assertFalse(h.received);
 
     cb.setValue(true, true);
     assertTrue(h.received);
+
+    try {
+      cb.setValue(null);
+      fail("Should throw IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      /* pass */
+    }
   }
-  
+
+  static class ListenerTester implements ClickListener {
+    static int fired = 0;
+    static HandlerManager manager;
+
+    public static void fire() {
+      fired = 0;
+      manager.fireEvent(new ClickEvent() {
+      });
+    }
+ 
+    public void onClick(Widget sender) {
+      ++fired;
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public void testListenerRemoval() {
+    CheckBox b = new CheckBox();
+ 
+    ClickListener r1 = new ListenerTester();
+    ClickListener r2 = new ListenerTester();
+    ListenerTester.manager = b.ensureHandlers();
+    b.addClickListener(r1);
+    b.addClickListener(r2);
+
+    ListenerTester.fire();
+    assertEquals(ListenerTester.fired, 2);
+
+    b.removeClickListener(r1);
+    ListenerTester.fire();
+    assertEquals(ListenerTester.fired, 1);
+
+    b.removeClickListener(r2);
+    ListenerTester.fire();
+    assertEquals(ListenerTester.fired, 0);
+  }
+
   private static class Handler implements ValueChangeHandler<Boolean> {
     Boolean received = null;
-    
+
     public void onValueChange(ValueChangeEvent<Boolean> event) {
       received = event.getValue();
     }