2012-07-02 17:16:55 -07:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2012-08-19 12:00:19 -07:00
|
|
|
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
2012-07-02 17:16:55 -07:00
|
|
|
|
2012-10-08 20:15:47 -07:00
|
|
|
const kSystemMessageInternalReady = "system-message-internal-ready";
|
|
|
|
|
2012-08-27 07:13:02 -07:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|
|
|
"@mozilla.org/childprocessmessagemanager;1",
|
|
|
|
"nsISyncMessageSender");
|
2012-07-02 17:16:55 -07:00
|
|
|
|
|
|
|
// Limit the number of pending messages for a given type.
|
|
|
|
let kMaxPendingMessages;
|
|
|
|
try {
|
|
|
|
kMaxPendingMessages = Services.prefs.getIntPref("dom.messages.maxPendingMessages");
|
|
|
|
} catch(e) {
|
|
|
|
// getIntPref throws when the pref is not set.
|
|
|
|
kMaxPendingMessages = 5;
|
|
|
|
}
|
|
|
|
|
2012-08-23 11:56:36 -07:00
|
|
|
function debug(aMsg) {
|
|
|
|
//dump("-- SystemMessageManager " + Date.now() + " : " + aMsg + "\n");
|
2012-07-02 17:16:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of the DOM API for system messages
|
|
|
|
|
|
|
|
function SystemMessageManager() {
|
|
|
|
// Message handlers for this page.
|
|
|
|
// We can have only one handler per message type.
|
|
|
|
this._handlers = {};
|
|
|
|
|
|
|
|
// Pending messages for this page, keyed by message type.
|
|
|
|
this._pendings = {};
|
2012-10-08 20:15:47 -07:00
|
|
|
|
|
|
|
// Flag to specify if this process has already registered manifest.
|
|
|
|
this._registerManifestReady = false;
|
|
|
|
|
|
|
|
// Flag to determine this process is a parent or child process.
|
|
|
|
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
|
|
|
|
this._isParentProcess =
|
|
|
|
!appInfo || appInfo.getService(Ci.nsIXULRuntime)
|
|
|
|
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
|
|
|
|
|
|
|
// An oberver to listen to whether the |SystemMessageInternal| is ready.
|
|
|
|
if (this._isParentProcess) {
|
|
|
|
Services.obs.addObserver(this, kSystemMessageInternalReady, false);
|
|
|
|
}
|
2012-07-02 17:16:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
SystemMessageManager.prototype = {
|
|
|
|
__proto__: DOMRequestIpcHelper.prototype,
|
|
|
|
|
2012-07-20 08:41:30 -07:00
|
|
|
_dispatchMessage: function sysMessMgr_dispatchMessage(aType, aHandler, aMessage) {
|
|
|
|
// We get a json blob, but in some cases we want another kind of object
|
|
|
|
// to be dispatched.
|
|
|
|
// To do so, we check if we have a with a contract ID of
|
|
|
|
// "@mozilla.org/dom/system-messages/wrapper/TYPE;1" component implementing
|
|
|
|
// nsISystemMessageWrapper.
|
|
|
|
debug("Dispatching " + JSON.stringify(aMessage) + "\n");
|
|
|
|
let contractID = "@mozilla.org/dom/system-messages/wrapper/" + aType + ";1";
|
2012-08-23 11:56:36 -07:00
|
|
|
let wrapped = false;
|
2012-07-20 08:41:30 -07:00
|
|
|
|
|
|
|
if (contractID in Cc) {
|
|
|
|
debug(contractID + " is registered, creating an instance");
|
|
|
|
let wrapper = Cc[contractID].createInstance(Ci.nsISystemMessagesWrapper);
|
|
|
|
if (wrapper) {
|
2012-08-24 21:07:58 -07:00
|
|
|
aMessage = wrapper.wrapMessage(aMessage, this._window);
|
2012-08-23 11:56:36 -07:00
|
|
|
wrapped = true;
|
2012-07-20 08:41:30 -07:00
|
|
|
debug("wrapped = " + aMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-08 19:54:50 -07:00
|
|
|
aHandler.handleMessage(wrapped ? aMessage
|
|
|
|
: ObjectWrapper.wrap(aMessage, this._window));
|
2012-07-20 08:41:30 -07:00
|
|
|
},
|
|
|
|
|
2012-07-02 17:16:55 -07:00
|
|
|
mozSetMessageHandler: function sysMessMgr_setMessageHandler(aType, aHandler) {
|
|
|
|
debug("setMessage handler for [" + aType + "] " + aHandler);
|
|
|
|
if (!aType) {
|
|
|
|
// Just bail out if we have no type.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let handlers = this._handlers;
|
|
|
|
if (!aHandler) {
|
|
|
|
// Setting the handler to null means we don't want to receive messages
|
|
|
|
// of this type anymore.
|
|
|
|
delete handlers[aType];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Last registered handler wins.
|
|
|
|
handlers[aType] = aHandler;
|
|
|
|
|
|
|
|
// If we have pending messages, send them asynchronously.
|
|
|
|
if (this._getPendingMessages(aType, true)) {
|
|
|
|
let thread = Services.tm.mainThread;
|
|
|
|
let pending = this._pendings[aType];
|
|
|
|
this._pendings[aType] = [];
|
2012-07-20 08:41:30 -07:00
|
|
|
let self = this;
|
2012-07-02 17:16:55 -07:00
|
|
|
pending.forEach(function dispatch_pending(aPending) {
|
|
|
|
thread.dispatch({
|
|
|
|
run: function run() {
|
2012-07-20 08:41:30 -07:00
|
|
|
self._dispatchMessage(aType, aHandler, aPending);
|
2012-07-02 17:16:55 -07:00
|
|
|
}
|
|
|
|
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPendingMessages: function sysMessMgr_getPendingMessages(aType, aForceUpdate) {
|
|
|
|
debug("hasPendingMessage " + aType);
|
|
|
|
let pendings = this._pendings;
|
|
|
|
|
|
|
|
// If we have a handler for this type, we can't have any pending message.
|
|
|
|
// If called from setMessageHandler, we still want to update the pending
|
|
|
|
// queue to deliver existing messages.
|
|
|
|
if (aType in this._handlers && !aForceUpdate) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-08-23 11:56:36 -07:00
|
|
|
// Send a sync message to the parent to check if we have a pending message
|
2012-07-02 17:16:55 -07:00
|
|
|
// for this type.
|
2012-10-08 19:54:50 -07:00
|
|
|
let messages = cpmm.sendSyncMessage("SystemMessageManager:GetPendingMessages",
|
2012-07-02 17:16:55 -07:00
|
|
|
{ type: aType,
|
|
|
|
uri: this._uri,
|
|
|
|
manifest: this._manifest })[0];
|
|
|
|
if (!messages) {
|
|
|
|
// No new pending messages, but the queue may not be empty yet.
|
|
|
|
return pendings[aType] && pendings[aType].length != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pendings[aType]) {
|
|
|
|
pendings[aType] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Doing that instead of pending.concat() to avoid array copy.
|
|
|
|
messages.forEach(function hpm_addPendings(aMessage) {
|
|
|
|
pendings[aType].push(aMessage);
|
|
|
|
if (pendings[aType].length > kMaxPendingMessages) {
|
|
|
|
pendings[aType].splice(0, 1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return pendings[aType].length != 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
mozHasPendingMessage: function sysMessMgr_hasPendingMessage(aType) {
|
|
|
|
return this._getPendingMessages(aType, false);
|
|
|
|
},
|
|
|
|
|
|
|
|
uninit: function sysMessMgr_uninit() {
|
|
|
|
this._handlers = null;
|
2012-10-08 20:15:47 -07:00
|
|
|
this._pendings = null;
|
|
|
|
|
|
|
|
if (this._isParentProcess) {
|
|
|
|
Services.obs.removeObserver(this, kSystemMessageInternalReady);
|
|
|
|
}
|
2012-07-02 17:16:55 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function sysMessMgr_receiveMessage(aMessage) {
|
2012-08-23 11:56:36 -07:00
|
|
|
debug("receiveMessage " + aMessage.name + " - " +
|
2012-07-02 17:16:55 -07:00
|
|
|
aMessage.json.type + " for " + aMessage.json.manifest +
|
|
|
|
" (" + this._manifest + ")");
|
|
|
|
|
|
|
|
let msg = aMessage.json;
|
|
|
|
if (msg.manifest != this._manifest)
|
|
|
|
return;
|
|
|
|
|
2012-10-08 19:54:50 -07:00
|
|
|
// 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 });
|
|
|
|
|
2012-07-02 17:16:55 -07:00
|
|
|
// Bail out if we have no handlers registered for this type.
|
|
|
|
if (!(msg.type in this._handlers)) {
|
|
|
|
debug("No handler for this type");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-20 08:41:30 -07:00
|
|
|
this._dispatchMessage(msg.type, this._handlers[msg.type], msg.msg);
|
2012-07-02 17:16:55 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// nsIDOMGlobalPropertyInitializer implementation.
|
|
|
|
init: function sysMessMgr_init(aWindow) {
|
|
|
|
debug("init");
|
|
|
|
this.initHelper(aWindow, ["SystemMessageManager:Message"]);
|
2012-08-13 09:58:38 -07:00
|
|
|
|
|
|
|
let principal = aWindow.document.nodePrincipal;
|
|
|
|
this._uri = principal.URI.spec;
|
|
|
|
|
|
|
|
let appsService = Cc["@mozilla.org/AppsService;1"]
|
|
|
|
.getService(Ci.nsIAppsService);
|
|
|
|
this._manifest = appsService.getManifestURLByLocalId(principal.appId);
|
2012-08-19 12:00:19 -07:00
|
|
|
this._window = aWindow;
|
2012-10-08 20:15:47 -07:00
|
|
|
|
|
|
|
// Two cases are valid to register the manifest for the current process:
|
|
|
|
// 1. This is asked by a child process (parent process must be ready).
|
|
|
|
// 2. Parent process has already constructed the |SystemMessageInternal|.
|
|
|
|
// Otherwise, delay to do it when the |SystemMessageInternal| is ready.
|
|
|
|
let readyToRegister = true;
|
|
|
|
if (this._isParentProcess) {
|
|
|
|
let ready = cpmm.sendSyncMessage(
|
|
|
|
"SystemMessageManager:AskReadyToRegister", null);
|
|
|
|
if (ready.length == 0 || !ready[0]) {
|
|
|
|
readyToRegister = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (readyToRegister) {
|
|
|
|
this._registerManifest();
|
|
|
|
}
|
|
|
|
|
2012-09-18 10:34:56 -07:00
|
|
|
debug("done");
|
2012-07-02 17:16:55 -07:00
|
|
|
},
|
|
|
|
|
2012-10-08 20:15:47 -07:00
|
|
|
observe: function sysMessMgr_observe(aSubject, aTopic, aData) {
|
|
|
|
if (aTopic === kSystemMessageInternalReady) {
|
|
|
|
this._registerManifest();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_registerManifest: function sysMessMgr_registerManifest() {
|
|
|
|
if (!this._registerManifestReady) {
|
|
|
|
cpmm.sendAsyncMessage("SystemMessageManager:Register",
|
|
|
|
{ manifest: this._manifest });
|
|
|
|
this._registerManifestReady = true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-07-02 17:16:55 -07:00
|
|
|
classID: Components.ID("{bc076ea0-609b-4d8f-83d7-5af7cbdc3bb2}"),
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMNavigatorSystemMessages,
|
2012-10-08 20:15:47 -07:00
|
|
|
Ci.nsIDOMGlobalPropertyInitializer,
|
|
|
|
Ci.nsIObserver]),
|
2012-07-02 17:16:55 -07:00
|
|
|
|
|
|
|
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"})
|
|
|
|
}
|
|
|
|
|
|
|
|
const NSGetFactory = XPCOMUtils.generateNSGetFactory([SystemMessageManager]);
|