Bug 1454038 - 2. Finalize event callbacks on content window unload; r?esawin
When the content window unloads, we will no longer get any callbacks
through the message manager, so we should finalize all active callbacks.
MozReview-Commit-ID: 3VJ2bTlhUmH
--- a/mobile/android/modules/geckoview/GeckoViewContentModule.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewContentModule.jsm
@@ -24,16 +24,23 @@ class GeckoViewContentModule {
constructor(aModuleName, aGlobal) {
this.moduleName = aModuleName;
this.messageManager = aGlobal;
if (!aGlobal._gvEventDispatcher) {
aGlobal._gvEventDispatcher =
GeckoViewUtils.getDispatcherForWindow(aGlobal.content);
+ aGlobal.addEventListener("unload", event => {
+ if (event.target === this.messageManager) {
+ aGlobal._gvEventDispatcher.finalize();
+ }
+ }, {
+ mozSystemGroup: true,
+ });
}
this.eventDispatcher = aGlobal._gvEventDispatcher;
this.messageManager.addMessageListener(
"GeckoView:UpdateSettings",
aMsg => {
this.settings = aMsg.data;
this.onSettingsUpdate();
--- a/mobile/android/modules/geckoview/Messaging.jsm
+++ b/mobile/android/modules/geckoview/Messaging.jsm
@@ -114,16 +114,30 @@ DispatcherDelegate.prototype = {
this.dispatch(type, aMsg, {
onSuccess: resolve,
onError: reject,
});
});
},
+ finalize: function() {
+ if (!this._replies) {
+ return;
+ }
+ this._replies.forEach(reply => {
+ if (typeof reply.finalizer === "function") {
+ reply.finalizer();
+ } else if (reply.finalizer) {
+ reply.finalizer.onFinalize();
+ }
+ });
+ this._replies.clear();
+ },
+
receiveMessage: function(aMsg) {
const {uuid, type} = aMsg.data;
const reply = this._replies.get(uuid);
if (!reply) {
return;
}
if (type === "success") {
@@ -182,20 +196,29 @@ var EventDispatcher = {
return new DispatcherDelegate(null, aMessageManager);
},
receiveMessage: function(aMsg) {
// aMsg.data includes keys: global, event, data, uuid
let callback;
if (aMsg.data.uuid) {
let reply = (type, response) => {
- let mm = aMsg.data.global ? aMsg.target : aMsg.target.messageManager;
+ const mm = aMsg.data.global ? aMsg.target : aMsg.target.messageManager;
+ if (!mm) {
+ if (type === "finalize") {
+ // It's normal for the finalize call to come after the browser has
+ // been destroyed. We can gracefully handle that case despite
+ // having no message manager.
+ return;
+ }
+ throw Error(`No message manager for ${aMsg.data.event}:${type} reply`);
+ }
mm.sendAsyncMessage("GeckoView:MessagingReply", {
- type: type,
- response: response,
+ type,
+ response,
uuid: aMsg.data.uuid,
});
};
callback = {
onSuccess: response => reply("success", response),
onError: error => reply("error", error),
onFinalize: () => reply("finalize"),
};