Fix for listener wrapper window listener removal, bug listed at:
http://gwt-code-reviews.appspot.com/603/show
Review by:jlabanca


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4298 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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/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 4421754..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,13 +104,13 @@
 
     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");
@@ -116,10 +118,47 @@
       /* 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();
     }