Bug 795782 - System Message API: Shouldn't pend messages for running apps to avoid re-firing them when restarting apps. r=fabrice

This commit is contained in:
Gene Lian 2012-10-09 10:54:50 +08:00
Родитель f118b7b673
Коммит f0638f5bc2
2 изменённых файлов: 106 добавлений и 42 удалений

Просмотреть файл

@ -16,6 +16,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
// Limit the number of pending messages for a given page.
let kMaxPendingMessages;
try {
@ -25,8 +29,9 @@ try {
kMaxPendingMessages = 5;
}
const kMessages =["SystemMessageManager:GetPending",
const kMessages =["SystemMessageManager:GetPendingMessages",
"SystemMessageManager:Register",
"SystemMessageManager:Message:Return:OK",
"child-process-shutdown"]
function debug(aMsg) {
@ -46,9 +51,9 @@ function SystemMessageInternal() {
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, "webapps-registry-ready", false);
kMessages.forEach((function(aMsg) {
kMessages.forEach(function(aMsg) {
ppmm.addMessageListener(aMsg, this);
}).bind(this));
}, this);
}
SystemMessageInternal.prototype = {
@ -64,25 +69,30 @@ SystemMessageInternal.prototype = {
return;
}
debug("Broadcasting " + aType + " " + JSON.stringify(aMessage));
// Give this message an ID so that we can identify the message and
// clean it up from the pending message queue when apps receive it.
let messageID = gUUIDGenerator.generateUUID().toString();
debug("Sending " + aType + " " + JSON.stringify(aMessage) +
" for " + aPageURI.spec + " @ " + aManifestURI.spec);
if (this._listeners[aManifestURI.spec]) {
this._listeners[aManifestURI.spec].forEach(function sendMsg(aListener) {
aListener.sendAsyncMessage("SystemMessageManager:Message",
{ type: aType,
msg: aMessage,
manifest: aManifestURI.spec })
manifest: aManifestURI.spec,
uri: aPageURI.spec,
msgID: messageID })
});
}
this._pages.forEach(function sendMess_openPage(aPage) {
if (aPage.type != aType ||
aPage.manifest != aManifestURI.spec ||
aPage.uri != aPageURI.spec) {
this._pages.forEach(function(aPage) {
if (!this._isPageMatched(aPage, aType, aPageURI.spec, aManifestURI.spec)) {
return;
}
this._processPage(aPage, aMessage);
}.bind(this))
this._openAppPage(aPage, aMessage, messageID);
}, this);
},
broadcastMessage: function broadcastMessage(aType, aMessage) {
@ -95,6 +105,10 @@ SystemMessageInternal.prototype = {
return;
}
// Give this message an ID so that we can identify the message and
// clean it up from the pending message queue when apps receive it.
let messageID = gUUIDGenerator.generateUUID().toString();
debug("Broadcasting " + aType + " " + JSON.stringify(aMessage));
// Find pages that registered an handler for this type.
this._pages.forEach(function(aPage) {
@ -104,12 +118,14 @@ SystemMessageInternal.prototype = {
aListener.sendAsyncMessage("SystemMessageManager:Message",
{ type: aType,
msg: aMessage,
manifest: aPage.manifest})
manifest: aPage.manifest,
uri: aPage.uri,
msgID: messageID })
});
}
this._processPage(aPage, aMessage);
this._openAppPage(aPage, aMessage, messageID);
}
}.bind(this))
}, this);
},
registerPage: function registerPage(aType, aPageURI, aManifestURI) {
@ -120,13 +136,14 @@ SystemMessageInternal.prototype = {
this._pages.push({ type: aType,
uri: aPageURI.spec,
manifest: aManifestURI.spec,
pending: [] });
pendingMessages: [] });
},
receiveMessage: function receiveMessage(aMessage) {
let msg = aMessage.json;
switch(aMessage.name) {
case "SystemMessageManager:Register":
{
let manifest = msg.manifest;
debug("Got Register from " + manifest);
if (!this._listeners[manifest]) {
@ -135,7 +152,9 @@ SystemMessageInternal.prototype = {
this._listeners[manifest].push(aMessage.target);
debug("listeners for " + manifest + " : " + this._listeners[manifest].length);
break;
}
case "child-process-shutdown":
{
debug("Got Unregister from " + aMessage.target);
let mm = aMessage.target;
for (let manifest in this._listeners) {
@ -147,41 +166,69 @@ SystemMessageInternal.prototype = {
}
}
break;
case "SystemMessageManager:GetPending":
debug("received SystemMessageManager:GetPending " + aMessage.json.type +
" for " + aMessage.json.uri + " @ " + aMessage.json.manifest);
// This is a sync call, use to return the pending message for a page.
debug(JSON.stringify(msg));
// Find the right page.
}
case "SystemMessageManager:GetPendingMessages":
{
debug("received SystemMessageManager:GetPendingMessages " + msg.type +
" for " + msg.uri + " @ " + msg.manifest);
// This is a sync call used to return the pending messages for a page.
// Find the right page to get its corresponding pending messages.
let page = null;
this._pages.some(function(aPage) {
if (aPage.uri == msg.uri &&
aPage.type == msg.type &&
aPage.manifest == msg.manifest) {
if (this._isPageMatched(aPage, msg.type, msg.uri, msg.manifest)) {
page = aPage;
}
return page !== null;
});
}, this);
if (!page) {
return null;
}
let pending = page.pending;
// Clear the pending queue for this page.
// This is ok since we'll store pending events in SystemMessageManager.js
page.pending = [];
// Return the |msg| of each pending message (drop the |msgID|).
let pendingMessages = [];
page.pendingMessages.forEach(function(aMessage) {
pendingMessages.push(aMessage.msg);
});
return pending;
// Clear the pending queue for this page. This is OK since we'll store
// pending messages in the content process (|SystemMessageManager|).
page.pendingMessages.length = 0;
return pendingMessages;
break;
}
case "SystemMessageManager:Message:Return:OK":
{
debug("received SystemMessageManager:Message:Return:OK " + msg.type +
" for " + msg.uri + " @ " + msg.manifest);
// We need to clean up the pending message since the app has already
// received it, thus avoiding the re-lanunched app handling it again.
this._pages.forEach(function(aPage) {
if (!this._isPageMatched(aPage, msg.type, msg.uri, msg.manifest)) {
return;
}
let pendingMessages = aPage.pendingMessages;
for (let i = 0; i < pendingMessages.length; i++) {
if (pendingMessages[i].msgID === msg.msgID) {
pendingMessages.splice(i, 1);
break;
}
}
}, this);
break;
}
}
},
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "xpcom-shutdown":
kMessages.forEach((function(aMsg) {
kMessages.forEach(function(aMsg) {
ppmm.removeMessageListener(aMsg, this);
}).bind(this));
}, this);
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.obs.removeObserver(this, "webapps-registry-ready");
ppmm = null;
@ -192,7 +239,7 @@ SystemMessageInternal.prototype = {
// After the webapps' registration has been done for sure,
// re-fire the buffered system messages if there is any.
this._webappsRegistryReady = true;
this._bufferedSysMsgs.forEach((function(aSysMsg) {
this._bufferedSysMsgs.forEach(function(aSysMsg) {
switch (aSysMsg.how) {
case "send":
this.sendMessage(
@ -202,17 +249,18 @@ SystemMessageInternal.prototype = {
this.broadcastMessage(aSysMsg.type, aSysMsg.msg);
break;
}
}).bind(this));
}, this);
this._bufferedSysMsgs = null;
break;
}
},
_processPage: function _processPage(aPage, aMessage) {
// Queue the message for the page.
aPage.pending.push(aMessage);
if (aPage.pending.length > kMaxPendingMessages) {
aPage.pending.splice(0, 1);
_openAppPage: function _openAppPage(aPage, aMessage, aMessageID) {
// Queue the message for this page because we've never known if an app is
// opened or not. We'll clean it up when the app has already received it.
aPage.pendingMessages.push({ msg: aMessage, msgID: aMessageID });
if (aPage.pendingMessages.length > kMaxPendingMessages) {
aPage.pendingMessages.splice(0, 1);
}
// We don't need to send the full object to observers.
@ -220,10 +268,16 @@ SystemMessageInternal.prototype = {
manifest: aPage.manifest,
type: aPage.type,
target: aMessage.target };
debug("Asking to open " + JSON.stringify(page));
debug("Asking to open " + JSON.stringify(page));
Services.obs.notifyObservers(this, "system-messages-open-app", JSON.stringify(page));
},
_isPageMatched: function _isPageMatched(aPage, aType, aUri, aManifest) {
return (aPage.type === aType &&
aPage.manifest === aManifest &&
aPage.uri === aUri)
},
classID: Components.ID("{70589ca5-91ac-4b9e-b839-d6a88167d714}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesInternal, Ci.nsIObserver])

Просмотреть файл

@ -64,7 +64,8 @@ SystemMessageManager.prototype = {
}
}
aHandler.handleMessage(wrapped ? aMessage : ObjectWrapper.wrap(aMessage, this._window));
aHandler.handleMessage(wrapped ? aMessage
: ObjectWrapper.wrap(aMessage, this._window));
},
mozSetMessageHandler: function sysMessMgr_setMessageHandler(aType, aHandler) {
@ -114,7 +115,7 @@ SystemMessageManager.prototype = {
// Send a sync message to the parent to check if we have a pending message
// for this type.
let messages = cpmm.sendSyncMessage("SystemMessageManager:GetPending",
let messages = cpmm.sendSyncMessage("SystemMessageManager:GetPendingMessages",
{ type: aType,
uri: this._uri,
manifest: this._manifest })[0];
@ -156,6 +157,15 @@ SystemMessageManager.prototype = {
if (msg.manifest != this._manifest)
return;
// Send an acknowledgement to parent to clean up the pending message,
// so a re-launched app won't handle it again, which is redundant.
cpmm.sendAsyncMessage(
"SystemMessageManager:Message:Return:OK",
{ type: msg.type,
manifest: msg.manifest,
uri: msg.uri,
msgID: msg.msgID });
// Bail out if we have no handlers registered for this type.
if (!(msg.type in this._handlers)) {
debug("No handler for this type");