Bug 1267693 P2 Avoid leaking windows via event channel closures. r=gabor a=sylvestre
authorBen Kelly <ben@wanderview.com>
Fri, 22 Jul 2016 10:17:50 -0700
changeset 340025 d0deaae22a870e4cdd3167b52ed8777335ddf700
parent 340024 2f9a3a609582d45b1adee9150ff86d3e3711f759
child 340026 ccbdbee8d758c6aaf43b22c1b745cc9a13d51a6e
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor, sylvestre
bugs1267693
milestone49.0a2
Bug 1267693 P2 Avoid leaking windows via event channel closures. r=gabor a=sylvestre
addon-sdk/source/lib/sdk/window/events.js
--- a/addon-sdk/source/lib/sdk/window/events.js
+++ b/addon-sdk/source/lib/sdk/window/events.js
@@ -2,32 +2,50 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { Ci } = require("chrome");
+const { Ci, Cu } = require("chrome");
 const { observe } = require("../event/chrome");
 const { open } = require("../event/dom");
 const { windows } = require("../window/utils");
 const { filter, merge, map, expand } = require("../event/utils");
 
+function documentMatches(weakWindow, event) {
+  let window = weakWindow.get();
+  return window && event.target === window.document;
+}
+
+function makeStrictDocumentFilter(window) {
+  // Note: Do not define a closure within this function.  Otherwise
+  //       you may leak the window argument.
+  let weak = Cu.getWeakReference(window);
+  return documentMatches.bind(null, weak);
+}
+
+function toEventWithDefaultViewTarget({type, target}) {
+  return { type: type, target: target.defaultView }
+}
+
 // Function registers single shot event listeners for relevant window events
 // that forward events to exported event stream.
 function eventsFor(window) {
+  // NOTE: Do no use pass a closure from this function into a stream
+  //       transform function.  You will capture the window in the
+  //       closure and leak the window until the event stream is
+  //       completely closed.
   let interactive = open(window, "DOMContentLoaded", { capture: true });
   let complete = open(window, "load", { capture: true });
   let states = merge([interactive, complete]);
-  let changes = filter(states, ({target}) => target === window.document);
-  return map(changes, function({type, target}) {
-    return { type: type, target: target.defaultView }
-  });
+  let changes = filter(states, makeStrictDocumentFilter(window));
+  return map(changes, toEventWithDefaultViewTarget);
 }
 
 // In addition to observing windows that are open we also observe windows
 // that are already already opened in case they're in process of loading.
 var opened = windows(null, { includePrivate: true });
 var currentEvents = merge(opened.map(eventsFor));
 
 // Register system event listeners for top level window open / close.