Fixes a memory leak in ScrollImplTrident by removing references from elements to GWT code.

http://gwt-code-reviews.appspot.com/1601803/

Issue: 7015
Author: stephen.haberman, dirk.scheffler
Review: jlabanca

Review by: rdayal@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10823 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/ScrollImpl.java b/user/src/com/google/gwt/user/client/ui/ScrollImpl.java
index 06e8a20..93cc023 100644
--- a/user/src/com/google/gwt/user/client/ui/ScrollImpl.java
+++ b/user/src/com/google/gwt/user/client/ui/ScrollImpl.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 
@@ -31,38 +32,64 @@
    */
   static class ScrollImplTrident extends ScrollImpl {
 
+    private static JavaScriptObject scrollHandler;
+
+    private static JavaScriptObject resizeHandler;
+
+    /**
+     * Creates static, leak-safe scroll/resize handlers.
+     */
+    private static native void initStaticHandlers() /*-{
+      // caches last scroll position
+      @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::scrollHandler = function() {
+        var scrollableElem = $wnd.event.srcElement;
+        scrollableElem.__lastScrollTop = scrollableElem.scrollTop;
+        scrollableElem.__lastScrollLeft = scrollableElem.scrollLeft;
+      };
+      // watches for resizes that should fire a fake scroll event
+      @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler = function() {
+        var scrollableElem = $wnd.event.srcElement;
+        if (scrollableElem.__isScrollContainer) {
+          scrollableElem = scrollableElem.parentNode;
+        }
+        // Give the browser a chance to fire a native scroll event before synthesizing one.
+        setTimeout($entry(function() {
+          // Trigger a synthetic scroll event if the scroll position changes.
+          if (scrollableElem.scrollTop != scrollableElem.__lastScrollTop ||
+              scrollableElem.scrollLeft != scrollableElem.__lastScrollLeft) {
+            // Update scroll positions.
+            scrollableElem.__lastScrollTop = scrollableElem.scrollTop;
+            scrollableElem.__lastScrollLeft = scrollableElem.scrollLeft;
+            @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::triggerScrollEvent(Lcom/google/gwt/dom/client/Element;)
+              (scrollableElem);
+          }
+        }), 1);
+      };
+    }-*/;
+
     private static void triggerScrollEvent(Element elem) {
       elem.dispatchEvent(Document.get().createScrollEvent());
     }
 
+    ScrollImplTrident() {
+      initStaticHandlers();
+    }
+
     @Override
     public native void initialize(Element scrollable, Element container) /*-{
       // Remember the last scroll position.
-      var scrollableElem = scrollable;
-      scrollableElem.__lastScrollTop = scrollableElem.__lastScrollLeft = 0;
-      var scrollHandler = $entry(function() {
-        scrollableElem.__lastScrollTop = scrollableElem.scrollTop;
-        scrollableElem.__lastScrollLeft = scrollableElem.scrollLeft;
-      });
-      scrollable.attachEvent('onscroll', scrollHandler);
+      scrollable.__lastScrollTop = scrollable.__lastScrollLeft = 0;
+      scrollable.attachEvent('onscroll',
+        @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::scrollHandler);
 
       // Detect if the scrollable element or the container within it changes
       // size, either of which could affect the scroll position.
-      var resizeHandler = $entry(function() {
-        // Give the browser a chance to fire a native scroll event before
-        // synthesizing one. 
-        setTimeout($entry(function() {
-          // Trigger a synthetic scroll event if the scroll position changes.
-          if (scrollableElem.scrollTop != scrollableElem.__lastScrollTop ||
-              scrollableElem.scrollLeft != scrollableElem.__lastScrollLeft) {
-            scrollHandler(); // Update scroll positions.
-            @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::triggerScrollEvent(Lcom/google/gwt/dom/client/Element;)
-              (scrollableElem);
-          }
-        }), 1);
-      });
-      scrollable.attachEvent('onresize', resizeHandler);
-      container.attachEvent('onresize', resizeHandler);
+      scrollable.attachEvent('onresize',
+        @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler);
+      container.attachEvent('onresize',
+        @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler);
+      // use boolean (primitive, won't leak) hint to resizeHandler that its the container
+      container.__isScrollContainer = true;
     }-*/;
 
     @Override