1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 * Helper object for APIs that deal with DOMRequests and need to release them
7 * when the window goes out of scope.
9 const Cu = Components.utils;
10 const Cc = Components.classes;
11 const Ci = Components.interfaces;
13 this.EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
15 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
16 Cu.import("resource://gre/modules/Services.jsm");
18 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
19 "@mozilla.org/childprocessmessagemanager;1",
20 "nsIMessageListenerManager");
23 * We use DOMRequestIpcHelperMessageListener to avoid leaking objects which
24 * "inherit" from DOMRequestIpcHelper.
26 * The issue is that the message manager will hold a strong ref to the message
27 * listener we register with it. But we don't want to hold a strong ref to the
28 * DOMRequestIpcHelper object, because that object may be arbitrarily large.
30 * So instead the message manager holds a strong ref to the
31 * DOMRequestIpcHelperMessageListener, and that holds a /weak/ ref to its
32 * DOMRequestIpcHelper.
34 * Additionally, we want to unhook all of these message listeners when the
35 * appropriate window is destroyed. We use DOMRequestIpcHelperMessageListener
38 this.DOMRequestIpcHelperMessageListener = function(aHelper, aWindow, aMessages) {
39 this._weakHelper = Cu.getWeakReference(aHelper);
41 this._messages = aMessages;
42 this._messages.forEach(function(msgName) {
43 cpmm.addMessageListener(msgName, this);
46 Services.obs.addObserver(this, "inner-window-destroyed", /* weakRef */ true);
48 // aWindow may be null; in that case, the DOMRequestIpcHelperMessageListener
49 // is not tied to a particular window and lives forever.
51 let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
52 .getInterface(Ci.nsIDOMWindowUtils);
53 this._innerWindowID = util.currentInnerWindowID;
57 DOMRequestIpcHelperMessageListener.prototype = {
58 QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
60 Ci.nsISupportsWeakReference]),
62 observe: function(aSubject, aTopic, aData) {
63 if (aTopic !== "inner-window-destroyed") {
67 let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
68 if (wId != this._innerWindowID) {
75 receiveMessage: function(aMsg) {
76 let helper = this._weakHelper.get();
78 helper.receiveMessage(aMsg);
85 // DOMRequestIpcHelper.destroy() calls back into this function.
86 if (this._destroyed) {
89 this._destroyed = true;
91 Services.obs.removeObserver(this, "inner-window-destroyed");
93 this._messages.forEach(function(msgName) {
94 cpmm.removeMessageListener(msgName, this);
96 this._messages = null;
98 let helper = this._weakHelper.get();
100 helper.destroyDOMRequestHelper();
105 this.DOMRequestIpcHelper = function DOMRequestIpcHelper() {
108 DOMRequestIpcHelper.prototype = {
110 * An object which "inherits" from DOMRequestIpcHelper and declares its own
111 * queryInterface method MUST implement Ci.nsISupportsWeakReference.
113 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
115 initDOMRequestHelper: function(aWindow, aMessages) {
116 this._DOMRequestIpcHelperMessageListener =
117 new DOMRequestIpcHelperMessageListener(this, aWindow, aMessages);
119 this._window = aWindow;
121 this._id = this._getRandomId();
124 // We don't use this.innerWindowID, but other classes rely on it.
125 let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
126 .getInterface(Ci.nsIDOMWindowUtils);
127 this.innerWindowID = util.currentInnerWindowID;
131 getRequestId: function(aRequest) {
132 let id = "id" + this._getRandomId();
133 this._requests[id] = aRequest;
137 getRequest: function(aId) {
138 if (this._requests[aId])
139 return this._requests[aId];
142 removeRequest: function(aId) {
143 if (this._requests[aId])
144 delete this._requests[aId];
147 takeRequest: function(aId) {
148 if (!this._requests[aId])
150 let request = this._requests[aId];
151 delete this._requests[aId];
155 _getRandomId: function() {
156 return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
159 destroyDOMRequestHelper: function() {
160 // This function is re-entrant --
161 // DOMRequestIpcHelperMessageListener.destroy() calls back into this
162 // function, and this.uninit() may also call it.
163 if (this._destroyed) {
166 this._destroyed = true;
168 this._DOMRequestIpcHelperMessageListener.destroy();
177 createRequest: function() {
178 return Services.DOMRequest.createRequest(this._window);