Bug 1449821 - 3. Add finalizer support in Messaging.jsm; r?esawin draft
authorJim Chen <nchen@mozilla.com>
Thu, 05 Apr 2018 18:50:11 -0400
changeset 778193 7d9c49f652c23c6ae32baaeaaa2f7423f2a6e3f6
parent 778192 a2a10096d5c4fa85d7b55c3e770426f0ed833156
child 778194 34bbbfc918b1911cf195b2e55e02839a3cb79228
push id105421
push userbmo:nchen@mozilla.com
push dateThu, 05 Apr 2018 22:53:25 +0000
reviewersesawin
bugs1449821
milestone61.0a1
Bug 1449821 - 3. Add finalizer support in Messaging.jsm; r?esawin Unregister the child process message listener in the finalizer so that memory is freed if callback is not invoked, and also so that callbacks can be invoked multiple times. MozReview-Commit-ID: 92HDFLzey9q
mobile/android/modules/geckoview/Messaging.jsm
--- a/mobile/android/modules/geckoview/Messaging.jsm
+++ b/mobile/android/modules/geckoview/Messaging.jsm
@@ -19,103 +19,108 @@ function DispatcherDelegate(aDispatcher,
   this._dispatcher = aDispatcher;
   this._messageManager = aMessageManager;
 }
 
 DispatcherDelegate.prototype = {
   /**
    * Register a listener to be notified of event(s).
    *
-   * @param listener Target listener implementing nsIAndroidEventListener.
-   * @param events   String or array of strings of events to listen to.
+   * @param aListener Target listener implementing nsIAndroidEventListener.
+   * @param aEvents   String or array of strings of events to listen to.
    */
-  registerListener: function(listener, events) {
+  registerListener: function(aListener, aEvents) {
     if (!this._dispatcher) {
       throw new Error("Can only listen in parent process");
     }
-    this._dispatcher.registerListener(listener, events);
+    this._dispatcher.registerListener(aListener, aEvents);
   },
 
   /**
    * Unregister a previously-registered listener.
    *
-   * @param listener Registered listener implementing nsIAndroidEventListener.
-   * @param events   String or array of strings of events to stop listening to.
+   * @param aListener Registered listener implementing nsIAndroidEventListener.
+   * @param aEvents   String or array of strings of events to stop listening to.
    */
-  unregisterListener: function(listener, events) {
+  unregisterListener: function(aListener, aEvents) {
     if (!this._dispatcher) {
       throw new Error("Can only listen in parent process");
     }
-    this._dispatcher.unregisterListener(listener, events);
+    this._dispatcher.unregisterListener(aListener, aEvents);
   },
 
   /**
    * Dispatch an event to registered listeners for that event, and pass an
    * optional data object and/or a optional callback interface to the
    * listeners.
    *
-   * @param event    Name of event to dispatch.
-   * @param data     Optional object containing data for the event.
-   * @param callback Optional callback implementing nsIAndroidEventCallback.
+   * @param aEvent     Name of event to dispatch.
+   * @param aData      Optional object containing data for the event.
+   * @param aCallback  Optional callback implementing nsIAndroidEventCallback.
+   * @param aFinalizer Optional finalizer implementing nsIAndroidEventFinalizer.
    */
-  dispatch: function(event, data, callback) {
+  dispatch: function(aEvent, aData, aCallback, aFinalizer) {
     if (this._dispatcher) {
-      this._dispatcher.dispatch(event, data, callback);
+      this._dispatcher.dispatch(aEvent, aData, aCallback, aFinalizer);
       return;
     }
 
     let mm = this._messageManager || Services.cpmm;
     let forwardData = {
       global: !this._messageManager,
-      event: event,
-      data: data,
+      event: aEvent,
+      data: aData,
     };
 
-    if (callback) {
+    if (aCallback) {
       forwardData.uuid = UUIDGen.generateUUID().toString();
       mm.addMessageListener("GeckoView:MessagingReply", function listener(msg) {
-        if (msg.data.uuid === forwardData.uuid) {
+        if (msg.data.uuid !== forwardData.uuid) {
+          return;
+        }
+        if (msg.data.type === "success") {
+          aCallback.onSuccess(msg.data.response);
+        } else if (msg.data.type === "error") {
+          aCallback.onError(msg.data.response);
+        } else if (msg.data.type === "finalize") {
+          aFinalizer && aFinalizer.onFinalize();
           mm.removeMessageListener(msg.name, listener);
-          if (msg.data.type === "success") {
-            callback.onSuccess(msg.data.response);
-          } else if (msg.data.type === "error") {
-            callback.onError(msg.data.response);
-          } else {
-            throw new Error("invalid reply type");
-          }
+        } else {
+          throw new Error("invalid reply type");
         }
       });
     }
 
     mm.sendAsyncMessage("GeckoView:Messaging", forwardData);
   },
 
   /**
    * Sends a request to Java.
    *
-   * @param msg Message to send; must be an object with a "type" property
+   * @param aMsg      Message to send; must be an object with a "type" property
+   * @param aCallback Optional callback implementing nsIAndroidEventCallback.
    */
-  sendRequest: function(msg, callback) {
-    let type = msg.type;
-    msg.type = undefined;
-    this.dispatch(type, msg, callback);
+  sendRequest: function(aMsg, aCallback) {
+    const type = aMsg.type;
+    aMsg.type = undefined;
+    this.dispatch(type, aMsg, aCallback);
   },
 
   /**
    * Sends a request to Java, returning a Promise that resolves to the response.
    *
-   * @param msg Message to send; must be an object with a "type" property
-   * @returns A Promise resolving to the response
+   * @param aMsg Message to send; must be an object with a "type" property
+   * @return A Promise resolving to the response
    */
-  sendRequestForResult: function(msg) {
+  sendRequestForResult: function(aMsg) {
     return new Promise((resolve, reject) => {
-      let type = msg.type;
-      msg.type = undefined;
+      const type = aMsg.type;
+      aMsg.type = undefined;
 
-      this.dispatch(type, msg, {
+      this.dispatch(type, aMsg, {
         onSuccess: resolve,
         onError: reject,
       });
     });
   },
 };
 
 var EventDispatcher = {
@@ -167,26 +172,28 @@ var EventDispatcher = {
           type: type,
           response: response,
           uuid: aMsg.data.uuid,
         });
       };
       callback = {
         onSuccess: response => reply("success", response),
         onError: error => reply("error", error),
+        onFinalize: () => reply("finalize"),
       };
     }
 
     if (aMsg.data.global) {
-      this.instance.dispatch(aMsg.data.event, aMsg.data.data.callback);
+      this.instance.dispatch(aMsg.data.event, aMsg.data.data,
+                             callback, callback);
       return;
     }
 
     let win = aMsg.target.ownerGlobal;
     let dispatcher = win.WindowEventDispatcher || this.for(win);
-    dispatcher.dispatch(aMsg.data.event, aMsg.data.data, callback);
+    dispatcher.dispatch(aMsg.data.event, aMsg.data.data, callback, callback);
   },
 };
 
 if (IS_PARENT_PROCESS) {
   Services.mm.addMessageListener("GeckoView:Messaging", EventDispatcher);
   Services.ppmm.addMessageListener("GeckoView:Messaging", EventDispatcher);
 }