michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // TODO Bug 907060 Per off-line discussion, after the MessagePort is done michael@0: // at Bug 643325, we will start to refactorize the common logic of both michael@0: // Inter-App Communication and Shared Worker. For now, we hope to design an michael@0: // MozInterAppMessagePort to meet the timeline, which still follows exactly michael@0: // the same interface and semantic as the MessagePort is. In the future, michael@0: // we can then align it back to MessagePort with backward compatibility. michael@0: michael@0: "use strict"; michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); michael@0: michael@0: const DEBUG = false; michael@0: function debug(aMsg) { michael@0: dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n"); michael@0: } michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "cpmm", michael@0: "@mozilla.org/childprocessmessagemanager;1", michael@0: "nsIMessageSender"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "appsService", michael@0: "@mozilla.org/AppsService;1", michael@0: "nsIAppsService"); michael@0: michael@0: const kMessages = ["InterAppMessagePort:OnMessage"]; michael@0: michael@0: function InterAppMessagePort() { michael@0: if (DEBUG) debug("InterAppMessagePort()"); michael@0: }; michael@0: michael@0: InterAppMessagePort.prototype = { michael@0: __proto__: DOMRequestIpcHelper.prototype, michael@0: michael@0: classDescription: "MozInterAppMessagePort", michael@0: michael@0: classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"), michael@0: michael@0: contractID: "@mozilla.org/dom/inter-app-message-port;1", michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer, michael@0: Ci.nsISupportsWeakReference, michael@0: Ci.nsIObserver]), michael@0: michael@0: // Ci.nsIDOMGlobalPropertyInitializer implementation. michael@0: init: function(aWindow) { michael@0: if (DEBUG) debug("Calling init()."); michael@0: michael@0: this.initDOMRequestHelper(aWindow, kMessages); michael@0: michael@0: let principal = aWindow.document.nodePrincipal; michael@0: this._manifestURL = appsService.getManifestURLByLocalId(principal.appId); michael@0: this._pageURL = principal.URI.specIgnoringRef; michael@0: michael@0: // Remove query string. michael@0: this._pageURL = this._pageURL.split("?")[0]; michael@0: michael@0: this._started = false; michael@0: this._closed = false; michael@0: this._messageQueue = []; michael@0: }, michael@0: michael@0: // WebIDL implementation for constructor. michael@0: __init: function(aMessagePortID) { michael@0: if (DEBUG) { michael@0: debug("Calling __init(): aMessagePortID: " + aMessagePortID); michael@0: } michael@0: michael@0: this._messagePortID = aMessagePortID; michael@0: michael@0: cpmm.sendAsyncMessage("InterAppMessagePort:Register", michael@0: { messagePortID: this._messagePortID, michael@0: manifestURL: this._manifestURL, michael@0: pageURL: this._pageURL }); michael@0: }, michael@0: michael@0: // DOMRequestIpcHelper implementation. michael@0: uninit: function() { michael@0: if (DEBUG) debug("Calling uninit()."); michael@0: michael@0: // When the message port is uninitialized, we need to disentangle the michael@0: // coupling ports, as if the close() method had been called. michael@0: if (this._closed) { michael@0: if (DEBUG) debug("close() has been called. Don't need to close again."); michael@0: return; michael@0: } michael@0: michael@0: this.close(); michael@0: }, michael@0: michael@0: postMessage: function(aMessage) { michael@0: if (DEBUG) debug("Calling postMessage()."); michael@0: michael@0: if (this._closed) { michael@0: if (DEBUG) debug("close() has been called. Cannot post message."); michael@0: return; michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage", michael@0: { messagePortID: this._messagePortID, michael@0: manifestURL: this._manifestURL, michael@0: message: aMessage }); michael@0: }, michael@0: michael@0: start: function() { michael@0: // Begin dispatching messages received on the port. michael@0: if (DEBUG) debug("Calling start()."); michael@0: michael@0: if (this._closed) { michael@0: if (DEBUG) debug("close() has been called. Cannot call start()."); michael@0: return; michael@0: } michael@0: michael@0: if (this._started) { michael@0: if (DEBUG) debug("start() has been called. Don't need to start again."); michael@0: return; michael@0: } michael@0: michael@0: // When a port's port message queue is enabled, the event loop must use it michael@0: // as one of its task sources. michael@0: this._started = true; michael@0: while (this._messageQueue.length) { michael@0: let message = this._messageQueue.shift(); michael@0: this._dispatchMessage(message); michael@0: } michael@0: }, michael@0: michael@0: close: function() { michael@0: // Disconnecting the port, so that it is no longer active. michael@0: if (DEBUG) debug("Calling close()."); michael@0: michael@0: if (this._closed) { michael@0: if (DEBUG) debug("close() has been called. Don't need to close again."); michael@0: return; michael@0: } michael@0: michael@0: this._closed = true; michael@0: this._messageQueue.length = 0; michael@0: michael@0: // When this method called on a local port that is entangled with another michael@0: // port, must cause the user agent to disentangle the coupling ports. michael@0: cpmm.sendAsyncMessage("InterAppMessagePort:Unregister", michael@0: { messagePortID: this._messagePortID, michael@0: manifestURL: this._manifestURL }); michael@0: }, michael@0: michael@0: get onmessage() { michael@0: if (DEBUG) debug("Getting onmessage handler."); michael@0: michael@0: return this.__DOM_IMPL__.getEventHandler("onmessage"); michael@0: }, michael@0: michael@0: set onmessage(aHandler) { michael@0: if (DEBUG) debug("Setting onmessage handler."); michael@0: michael@0: this.__DOM_IMPL__.setEventHandler("onmessage", aHandler); michael@0: michael@0: // The first time a MessagePort object's onmessage IDL attribute is set, michael@0: // the port's message queue must be enabled, as if the start() method had michael@0: // been called. michael@0: if (this._started) { michael@0: if (DEBUG) debug("start() has been called. Don't need to start again."); michael@0: return; michael@0: } michael@0: michael@0: this.start(); michael@0: }, michael@0: michael@0: _dispatchMessage: function _dispatchMessage(aMessage) { michael@0: let wrappedMessage = Cu.cloneInto(aMessage, this._window); michael@0: if (DEBUG) { michael@0: debug("_dispatchMessage: wrappedMessage: " + michael@0: JSON.stringify(wrappedMessage)); michael@0: } michael@0: michael@0: let event = new this._window michael@0: .MozInterAppMessageEvent("message", michael@0: { data: wrappedMessage }); michael@0: this.__DOM_IMPL__.dispatchEvent(event); michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: if (DEBUG) debug("receiveMessage: name: " + aMessage.name); michael@0: michael@0: let message = aMessage.json; michael@0: if (message.manifestURL != this._manifestURL || michael@0: message.pageURL != this._pageURL || michael@0: message.messagePortID != this._messagePortID) { michael@0: if (DEBUG) debug("The message doesn't belong to this page. Returning."); michael@0: return; michael@0: } michael@0: michael@0: switch (aMessage.name) { michael@0: case "InterAppMessagePort:OnMessage": michael@0: if (this._closed) { michael@0: if (DEBUG) debug("close() has been called. Drop the message."); michael@0: return; michael@0: } michael@0: michael@0: if (!this._started) { michael@0: if (DEBUG) debug("Not yet called start(). Queue up the message."); michael@0: this._messageQueue.push(message.message); michael@0: return; michael@0: } michael@0: michael@0: this._dispatchMessage(message.message); michael@0: break; michael@0: michael@0: default: michael@0: if (DEBUG) debug("Error! Shouldn't fall into this case."); michael@0: break; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort]); michael@0: