Bug 889984 - Don't leak objects which "inherit" from DOMRequestIpcHelper the associated window is closed. r=fabrice
authorJustin Lebar <justin.lebar@gmail.com>
Mon, 08 Jul 2013 17:55:42 -0400
changeset 137712 4b00bb32356d5d9b4b8e605656b57d276bd1db76
parent 137711 c815ed66c5aec8bd9742383ced5eac8c1faaf6ea
child 137713 51c9d3492d7ffece2d9c7fa26c8cfd99ccc071f2
push id24933
push useremorley@mozilla.com
push dateTue, 09 Jul 2013 07:51:23 +0000
treeherdermozilla-central@81dbb9d8c142 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs889984
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 889984 - Don't leak objects which "inherit" from DOMRequestIpcHelper the associated window is closed. r=fabrice
dom/alarm/AlarmsManager.js
dom/apps/src/Webapps.js
dom/base/DOMRequestHelper.jsm
dom/contacts/ContactManager.js
dom/fm/DOMFMRadioChild.js
dom/messages/SystemMessageManager.js
dom/network/src/NetworkStatsManager.js
dom/payment/Payment.js
dom/push/src/Push.js
dom/system/gonk/RILContentHelper.js
dom/wifi/DOMWifiManager.js
--- a/dom/alarm/AlarmsManager.js
+++ b/dom/alarm/AlarmsManager.js
@@ -30,17 +30,19 @@ function AlarmsManager()
 }
 
 AlarmsManager.prototype = {
 
   __proto__: DOMRequestIpcHelper.prototype,
 
   classID : ALARMSMANAGER_CID,
 
-  QueryInterface : XPCOMUtils.generateQI([nsIDOMMozAlarmsManager, Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface : XPCOMUtils.generateQI([nsIDOMMozAlarmsManager,
+                                          Ci.nsIDOMGlobalPropertyInitializer,
+                                          Ci.nsISupportsWeakReference]),
 
   classInfo : XPCOMUtils.generateCI({ classID: ALARMSMANAGER_CID,
                                       contractID: ALARMSMANAGER_CONTRACTID,
                                       classDescription: "AlarmsManager",
                                       interfaces: [nsIDOMMozAlarmsManager],
                                       flags: nsIClassInfo.DOM_OBJECT }),
 
   add: function add(aDate, aRespectTimezone, aData) {
@@ -157,17 +159,17 @@ AlarmsManager.prototype = {
     let principal = aWindow.document.nodePrincipal;
     let perm = Services.perms.testExactPermissionFromPrincipal(principal, "alarms");
     if (perm != Ci.nsIPermissionManager.ALLOW_ACTION)
       return null;
 
     this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
     // Add the valid messages to be listened.
-    this.initHelper(aWindow, ["AlarmsManager:Add:Return:OK", "AlarmsManager:Add:Return:KO", 
+    this.initDOMRequestHelper(aWindow, ["AlarmsManager:Add:Return:OK", "AlarmsManager:Add:Return:KO",
                               "AlarmsManager:GetAll:Return:OK", "AlarmsManager:GetAll:Return:KO"]);
 
     // Get the manifest URL if this is an installed app
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
     this._pageURL = principal.URI.spec;
     this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
     this._window = aWindow;
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -237,17 +237,17 @@ WebappsRegistry.prototype = {
                             appId: principal.appId,
                             isBrowser: principal.isInBrowserElement
                           });
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
-    this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
+    this.initDOMRequestHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
                               "Webapps:GetInstalled:Return:OK",
                               "Webapps:GetSelf:Return:OK",
                               "Webapps:CheckInstalled:Return:OK" ]);
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
@@ -259,17 +259,18 @@ WebappsRegistry.prototype = {
 
     // Only pages with the webapps-manage permission set can get access to
     // the mgmt object.
     this.hasMgmtPrivilege = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
   },
 
   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
+                                         Ci.mozIDOMApplicationRegistry,
 #ifdef MOZ_B2G
                                          Ci.mozIDOMApplicationRegistry2,
 #elifdef MOZ_WIDGET_ANDROID
                                          Ci.mozIDOMApplicationRegistry2,
 #endif
                                          Ci.nsIDOMGlobalPropertyInitializer]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
@@ -359,17 +360,17 @@ WebappsApplication.prototype = {
     this._onprogress = null;
     this._ondownloadsuccess = null;
     this._ondownloaderror = null;
     this._ondownloadavailable = null;
     this._ondownloadapplied = null;
 
     this._downloadError = null;
 
-    this.initHelper(aWindow, ["Webapps:OfflineCache",
+    this.initDOMRequestHelper(aWindow, ["Webapps:OfflineCache",
                               "Webapps:CheckForUpdate:Return:OK",
                               "Webapps:CheckForUpdate:Return:KO",
                               "Webapps:Launch:Return:OK",
                               "Webapps:Launch:Return:KO",
                               "Webapps:PackageEvent",
                               "Webapps:ClearBrowserData:Return"]);
 
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
@@ -631,30 +632,31 @@ WebappsApplication.prototype = {
       case "Webapps:ClearBrowserData:Return":
         Services.DOMRequest.fireSuccess(req, null);
         break;
     }
   },
 
   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication,
+                                         Ci.nsISupportsWeakReference]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
                                     contractID: "@mozilla.org/webapps/application;1",
                                     interfaces: [Ci.mozIDOMApplication],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application"})
 }
 
 /**
   * mozIDOMApplicationMgmt object
   */
 function WebappsApplicationMgmt(aWindow) {
-  this.initHelper(aWindow, ["Webapps:GetAll:Return:OK",
+  this.initDOMRequestHelper(aWindow, ["Webapps:GetAll:Return:OK",
                             "Webapps:GetAll:Return:KO",
                             "Webapps:Uninstall:Return:OK",
                             "Webapps:Uninstall:Broadcast:Return:OK",
                             "Webapps:Uninstall:Return:KO",
                             "Webapps:Install:Return:OK",
                             "Webapps:GetNotInstalled:Return:OK"]);
 
   cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
@@ -782,17 +784,17 @@ WebappsApplicationMgmt.prototype = {
     }
     if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") {
       this.removeRequest(msg.requestID);
     }
   },
 
   classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt, Ci.nsISupportsWeakReference]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
                                     contractID: "@mozilla.org/webapps/application-mgmt;1",
                                     interfaces: [Ci.mozIDOMApplicationMgmt],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application Mgmt"})
 }
 
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -1,33 +1,132 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 /**
-  * helper object for APIs that deal with DOMRequest and need to release them properly
-  * when the window goes out of scope
-  */
-const Cu = Components.utils; 
+ * Helper object for APIs that deal with DOMRequests and need to release them
+ * when the window goes out of scope.
+ */
+const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
+/**
+ * We use DOMRequestIpcHelperMessageListener to avoid leaking objects which
+ * "inherit" from DOMRequestIpcHelper.
+ *
+ * The issue is that the message manager will hold a strong ref to the message
+ * listener we register with it.  But we don't want to hold a strong ref to the
+ * DOMRequestIpcHelper object, because that object may be arbitrarily large.
+ *
+ * So instead the message manager holds a strong ref to the
+ * DOMRequestIpcHelperMessageListener, and that holds a /weak/ ref to its
+ * DOMRequestIpcHelper.
+ *
+ * Additionally, we want to unhook all of these message listeners when the
+ * appropriate window is destroyed.  We use DOMRequestIpcHelperMessageListener
+ * for this, too.
+ */
+this.DOMRequestIpcHelperMessageListener = function(aHelper, aWindow, aMessages) {
+  this._weakHelper = Cu.getWeakReference(aHelper);
+
+  this._messages = aMessages;
+  this._messages.forEach(function(msgName) {
+    cpmm.addMessageListener(msgName, this);
+  }, this);
+
+  Services.obs.addObserver(this, "inner-window-destroyed", /* weakRef */ true);
+
+  // aWindow may be null; in that case, the DOMRequestIpcHelperMessageListener
+  // is not tied to a particular window and lives forever.
+  if (aWindow) {
+    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
+    this._innerWindowID = util.currentInnerWindowID;
+  }
+}
+
+DOMRequestIpcHelperMessageListener.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
+                                         Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
+
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic !== "inner-window-destroyed") {
+      return;
+    }
+
+    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+    if (wId != this._innerWindowID) {
+      return;
+    }
+
+    this.destroy();
+  },
+
+  receiveMessage: function(aMsg) {
+    let helper = this._weakHelper.get();
+    if (helper) {
+      helper.receiveMessage(aMsg);
+    } else {
+      this.destroy();
+    }
+  },
+
+  destroy: function() {
+    Services.obs.removeObserver(this, "inner-window-destroyed");
+
+    this._messages.forEach(function(msgName) {
+      cpmm.removeMessageListener(msgName, this);
+    }, this);
+    this._messages = null;
+
+    let helper = this._weakHelper.get();
+    if (helper) {
+      helper.destroyDOMRequestHelper();
+    }
+  }
+}
+
 this.DOMRequestIpcHelper = function DOMRequestIpcHelper() {
 }
 
 DOMRequestIpcHelper.prototype = {
+  /**
+   * An object which "inherits" from DOMRequestIpcHelper and declares its own
+   * queryInterface method MUST implement Ci.nsISupportsWeakReference.
+   */
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+
+  initDOMRequestHelper: function(aWindow, aMessages) {
+    this._DOMRequestIpcHelperMessageListener =
+      new DOMRequestIpcHelperMessageListener(this, aWindow, aMessages);
+
+    this._window = aWindow;
+    this._requests = [];
+    this._id = this._getRandomId();
+
+    if (this._window) {
+      // We don't use this.innerWindowID, but other classes rely on it.
+      let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+      this.innerWindowID = util.currentInnerWindowID;
+    }
+  },
+
   getRequestId: function(aRequest) {
     let id = "id" + this._getRandomId();
     this._requests[id] = aRequest;
     return id;
   },
 
   getRequest: function(aId) {
     if (this._requests[aId])
@@ -46,56 +145,30 @@ DOMRequestIpcHelper.prototype = {
     delete this._requests[aId];
     return request;
   },
 
   _getRandomId: function() {
     return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   },
 
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic !== "inner-window-destroyed") {
+  destroyDOMRequestHelper: function() {
+    // This function is re-entrant --
+    // DOMRequestIpcHelperMessageListener.destroy() calls back into this
+    // function, and this.uninit() may also call it.
+    if (this._destroyed) {
       return;
     }
-
-    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    if (wId == this.innerWindowID) {
-      Services.obs.removeObserver(this, "inner-window-destroyed");
-      this._requests = [];
-      this._window = null;
-      this.removeMessageListener();
-      if(this.uninit)
-        this.uninit();
-    }
-  },
-
-  initRequests: function initRequests() {
-    this._requests = [];
-  },
+    this._destroyed = true;
 
-  initMessageListener: function initMessageListener(aMessages) {
-    this._messages = aMessages;
-    this._messages.forEach(function(msgName) {
-      cpmm.addMessageListener(msgName, this);
-    }, this);
-  },
-  
-  initHelper: function(aWindow, aMessages) {
-    this.initMessageListener(aMessages);
-    this.initRequests();
-    this._id = this._getRandomId();
-    Services.obs.addObserver(this, "inner-window-destroyed", false);
-    this._window = aWindow;
-    let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-    this.innerWindowID = util.currentInnerWindowID;
-  },
+    this._DOMRequestIpcHelperMessageListener.destroy();
+    this._requests = [];
+    this._window = null;
 
-  removeMessageListener: function removeMessageListener() {
-    this._messages.forEach(function(msgName) {
-      cpmm.removeMessageListener(msgName, this);
-    }, this);
-    this._messages = null;
+    if(this.uninit) {
+      this.uninit();
+    }
   },
 
   createRequest: function() {
     return Services.DOMRequest.createRequest(this._window);
   }
 }
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -906,17 +906,17 @@ ContactManager.prototype = {
       Services.DOMRequest.fireError(request);
     };
 
     this.askPermission("count", request, allowCallback, cancelCallback);
     return request;
   },
 
   init: function(aWindow) {
-    this.initHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
+    this.initDOMRequestHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
                               "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
                               "Contact:Save:Return:OK", "Contact:Save:Return:KO",
                               "Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
                               "Contact:Changed",
                               "PermissionPromptHelper:AskPermission:OK",
                               "Contacts:GetAll:Next", "Contacts:Revision",
                               "Contacts:Count"]);
   },
@@ -924,17 +924,19 @@ ContactManager.prototype = {
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) debug("uninit call");
     if (this._oncontactchange)
       this._oncontactchange = null;
   },
 
   classID : CONTACTMANAGER_CID,
-  QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager, Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager,
+                                          Ci.nsIDOMGlobalPropertyInitializer,
+                                          Ci.nsISupportsWeakReference]),
 
   classInfo : XPCOMUtils.generateCI({classID: CONTACTMANAGER_CID,
                                      contractID: CONTACTMANAGER_CONTRACTID,
                                      classDescription: "ContactManager",
                                      interfaces: [nsIDOMContactManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
 }
 
--- a/dom/fm/DOMFMRadioChild.js
+++ b/dom/fm/DOMFMRadioChild.js
@@ -37,17 +37,18 @@ DOMFMRadioChild.prototype = {
                classID: DOMFMMANAGER_CID,
                contractID: DOMFMMANAGER_CONTRACTID,
                classDescription: "DOMFMRadio",
                interfaces: [Ci.nsIDOMFMRadio],
                flags: Ci.nsIClassInfo.DOM_OBJECT
              }),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMFMRadio,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+                                         Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsISupportsWeakReference]),
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
                    .getService(Ci.nsIScriptSecurityManager);
 
     let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "fmradio");
     this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
@@ -67,17 +68,17 @@ DOMFMRadioChild.prototype = {
                       "DOMFMRadio:seekUp:Return:NO",
                       "DOMFMRadio:seekDown:Return:OK",
                       "DOMFMRadio:seekDown:Return:NO",
                       "DOMFMRadio:cancelSeek:Return:OK",
                       "DOMFMRadio:cancelSeek:Return:NO",
                       "DOMFMRadio:frequencyChange",
                       "DOMFMRadio:powerStateChange",
                       "DOMFMRadio:antennaChange"];
-    this.initHelper(aWindow, messages);
+    this.initDOMRequestHelper(aWindow, messages);
 
     let els = Cc["@mozilla.org/eventlistenerservice;1"]
                 .getService(Ci.nsIEventListenerService);
 
     els.addSystemEventListener(aWindow, "visibilitychange",
                                this._updateVisibility.bind(this),
                                /* useCapture = */ true);
 
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -243,17 +243,17 @@ SystemMessageManager.prototype = {
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   // nsIDOMGlobalPropertyInitializer implementation.
   init: function sysMessMgr_init(aWindow) {
     debug("init");
-    this.initHelper(aWindow, ["SystemMessageManager:Message",
+    this.initDOMRequestHelper(aWindow, ["SystemMessageManager:Message",
                               "SystemMessageManager:GetPendingMessages:Return"]);
 
     let principal = aWindow.document.nodePrincipal;
     this._isInBrowserElement = principal.isInBrowserElement;
     this._uri = principal.URI.spec;
 
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
@@ -302,17 +302,18 @@ SystemMessageManager.prototype = {
       this._registerManifestReady = true;
     }
   },
 
   classID: Components.ID("{bc076ea0-609b-4d8f-83d7-5af7cbdc3bb2}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMNavigatorSystemMessages,
                                          Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsIObserver]),
+                                         Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
 
   classInfo: XPCOMUtils.generateCI({
     classID: Components.ID("{bc076ea0-609b-4d8f-83d7-5af7cbdc3bb2}"),
     contractID: "@mozilla.org/system-message-manager;1",
     interfaces: [Ci.nsIDOMNavigatorSystemMessages],
     flags: Ci.nsIClassInfo.DOM_OBJECT,
     classDescription: "System Messages"})
 }
--- a/dom/network/src/NetworkStatsManager.js
+++ b/dom/network/src/NetworkStatsManager.js
@@ -214,30 +214,31 @@ NetworkStatsManager.prototype = {
     if (DEBUG) {
       debug("has privileges: " + this.hasPrivileges);
     }
 
     if (!this.hasPrivileges) {
       return null;
     }
 
-    this.initHelper(aWindow, ["NetworkStats:Get:Return",
+    this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
                               "NetworkStats:Clear:Return"]);
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) {
       debug("uninit call");
     }
   },
 
   classID : NETWORKSTATSMANAGER_CID,
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+                                         Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsISupportsWeakReference]),
 
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
                                      contractID: NETWORKSTATSMANAGER_CONTRACTID,
                                      classDescription: "NetworkStatsManager",
                                      interfaces: [nsIDOMMozNetworkStatsManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
 }
 
--- a/dom/payment/Payment.js
+++ b/dom/payment/Payment.js
@@ -26,17 +26,18 @@ function debug (s) {
 
 function PaymentContentHelper() {
 };
 
 PaymentContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINavigatorPayment,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+                                         Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsISupportsWeakReference]),
   classID:        PAYMENTCONTENTHELPER_CID,
   classInfo:      XPCOMUtils.generateCI({
     classID: PAYMENTCONTENTHELPER_CID,
     contractID: "@mozilla.org/payment/content-helper;1",
     classDescription: "Payment Content Helper",
     flags: Ci.nsIClassInfo.DOM_OBJECT,
     interfaces: [Ci.nsINavigatorPayment]
   }),
@@ -73,17 +74,17 @@ PaymentContentHelper.prototype = {
     });
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer
 
   init: function(aWindow) {
     this._window = aWindow;
-    this.initHelper(aWindow, PAYMENT_IPC_MSG_NAMES);
+    this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES);
     return this.pay.bind(this);
   },
 
   // nsIFrameMessageListener
 
   receiveMessage: function receiveMessage(aMessage) {
     let name = aMessage.name;
     let msg = aMessage.json;
--- a/dom/push/src/Push.js
+++ b/dom/push/src/Push.js
@@ -29,17 +29,18 @@ function Push()
   debug("Push Constructor");
 }
 
 Push.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   classID : PUSH_CID,
 
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
+                                          Ci.nsISupportsWeakReference]),
 
   init: function(aWindow) {
     debug("init()");
 
     if (!Services.prefs.getBoolPref("services.push.enabled"))
       return null;
 
     let principal = aWindow.document.nodePrincipal;
@@ -53,19 +54,17 @@ Push.prototype = {
     if (!this._manifestURL)
       return null;
 
     let perm = Services.perms.testExactPermissionFromPrincipal(principal,
                                                                "push");
     if (perm != Ci.nsIPermissionManager.ALLOW_ACTION)
       return null;
 
-    this.initHelper(aWindow, []);
-
-    this.initMessageListener([
+    this.initDOMRequestHelper(aWindow, [
       "PushService:Register:OK",
       "PushService:Register:KO",
       "PushService:Unregister:OK",
       "PushService:Unregister:KO",
       "PushService:Registrations:OK",
       "PushService:Registrations:KO"
     ]);
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -370,31 +370,31 @@ function RILContentHelper() {
     retryCount:           0,
     networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
     iccInfo:              new MobileICCInfo(),
     voiceConnectionInfo:  new MobileConnectionInfo(),
     dataConnectionInfo:   new MobileConnectionInfo()
   };
   this.voicemailInfo = new VoicemailInfo();
 
-  this.initRequests();
-  this.initMessageListener(RIL_IPC_MSG_NAMES);
+  this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
   Services.obs.addObserver(this, "xpcom-shutdown", false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider,
                                          Ci.nsICellBroadcastProvider,
                                          Ci.nsIVoicemailProvider,
                                          Ci.nsITelephonyProvider,
                                          Ci.nsIIccProvider,
-                                         Ci.nsIObserver]),
+                                         Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
   classID:   RILCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
                                     classDescription: "RILContentHelper",
                                     interfaces: [Ci.nsIMobileConnectionProvider,
                                                  Ci.nsICellBroadcastProvider,
                                                  Ci.nsIVoicemailProvider,
                                                  Ci.nsITelephonyProvider,
                                                  Ci.nsIIccProvider]}),
@@ -1231,17 +1231,17 @@ RILContentHelper.prototype = {
       data: value
     });
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     if (topic == "xpcom-shutdown") {
-      this.removeMessageListener();
+      this.destroyDOMRequestHelper();
       Services.obs.removeObserver(this, "xpcom-shutdown");
     }
   },
 
   // nsIMessageListener
 
   fireRequestSuccess: function fireRequestSuccess(requestId, result) {
     let request = this.takeRequest(requestId);
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -51,17 +51,18 @@ DOMWifiManager.prototype = {
   classID:   DOMWIFIMANAGER_CID,
   classInfo: XPCOMUtils.generateCI({classID: DOMWIFIMANAGER_CID,
                                     contractID: DOMWIFIMANAGER_CONTRACTID,
                                     classDescription: "DOMWifiManager",
                                     interfaces: [Ci.nsIDOMWifiManager],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMWifiManager,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+                                         Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsISupportsWeakReference]),
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
     let principal = aWindow.document.nodePrincipal;
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
     let perm = principal == secMan.getSystemPrincipal()
                  ? Ci.nsIPermissionManager.ALLOW_ACTION
@@ -88,17 +89,17 @@ DOMWifiManager.prototype = {
                       "WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
                       "WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
                       "WifiManager:wifiDown", "WifiManager:wifiUp",
                       "WifiManager:onconnecting", "WifiManager:onassociate",
                       "WifiManager:onconnect", "WifiManager:ondisconnect",
                       "WifiManager:onwpstimeout", "WifiManager:onwpsfail",
                       "WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate",
                       "WifiManager:onconnectingfailed"];
-    this.initHelper(aWindow, messages);
+    this.initDOMRequestHelper(aWindow, messages);
     this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
     var state = this._mm.sendSyncMessage("WifiManager:getState")[0];
     if (state) {
       this._currentNetwork = state.network;
       if (this._currentNetwork)
         exposeCurrentNetwork(this._currentNetwork);
       this._lastConnectionInfo = state.connectionInfo;