1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/apps/src/InterAppMessagePort.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,224 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// TODO Bug 907060 Per off-line discussion, after the MessagePort is done 1.9 +// at Bug 643325, we will start to refactorize the common logic of both 1.10 +// Inter-App Communication and Shared Worker. For now, we hope to design an 1.11 +// MozInterAppMessagePort to meet the timeline, which still follows exactly 1.12 +// the same interface and semantic as the MessagePort is. In the future, 1.13 +// we can then align it back to MessagePort with backward compatibility. 1.14 + 1.15 +"use strict"; 1.16 + 1.17 +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; 1.18 + 1.19 +Cu.import("resource://gre/modules/Services.jsm"); 1.20 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.21 +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); 1.22 + 1.23 +const DEBUG = false; 1.24 +function debug(aMsg) { 1.25 + dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n"); 1.26 +} 1.27 + 1.28 +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", 1.29 + "@mozilla.org/childprocessmessagemanager;1", 1.30 + "nsIMessageSender"); 1.31 + 1.32 +XPCOMUtils.defineLazyServiceGetter(this, "appsService", 1.33 + "@mozilla.org/AppsService;1", 1.34 + "nsIAppsService"); 1.35 + 1.36 +const kMessages = ["InterAppMessagePort:OnMessage"]; 1.37 + 1.38 +function InterAppMessagePort() { 1.39 + if (DEBUG) debug("InterAppMessagePort()"); 1.40 +}; 1.41 + 1.42 +InterAppMessagePort.prototype = { 1.43 + __proto__: DOMRequestIpcHelper.prototype, 1.44 + 1.45 + classDescription: "MozInterAppMessagePort", 1.46 + 1.47 + classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"), 1.48 + 1.49 + contractID: "@mozilla.org/dom/inter-app-message-port;1", 1.50 + 1.51 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer, 1.52 + Ci.nsISupportsWeakReference, 1.53 + Ci.nsIObserver]), 1.54 + 1.55 + // Ci.nsIDOMGlobalPropertyInitializer implementation. 1.56 + init: function(aWindow) { 1.57 + if (DEBUG) debug("Calling init()."); 1.58 + 1.59 + this.initDOMRequestHelper(aWindow, kMessages); 1.60 + 1.61 + let principal = aWindow.document.nodePrincipal; 1.62 + this._manifestURL = appsService.getManifestURLByLocalId(principal.appId); 1.63 + this._pageURL = principal.URI.specIgnoringRef; 1.64 + 1.65 + // Remove query string. 1.66 + this._pageURL = this._pageURL.split("?")[0]; 1.67 + 1.68 + this._started = false; 1.69 + this._closed = false; 1.70 + this._messageQueue = []; 1.71 + }, 1.72 + 1.73 + // WebIDL implementation for constructor. 1.74 + __init: function(aMessagePortID) { 1.75 + if (DEBUG) { 1.76 + debug("Calling __init(): aMessagePortID: " + aMessagePortID); 1.77 + } 1.78 + 1.79 + this._messagePortID = aMessagePortID; 1.80 + 1.81 + cpmm.sendAsyncMessage("InterAppMessagePort:Register", 1.82 + { messagePortID: this._messagePortID, 1.83 + manifestURL: this._manifestURL, 1.84 + pageURL: this._pageURL }); 1.85 + }, 1.86 + 1.87 + // DOMRequestIpcHelper implementation. 1.88 + uninit: function() { 1.89 + if (DEBUG) debug("Calling uninit()."); 1.90 + 1.91 + // When the message port is uninitialized, we need to disentangle the 1.92 + // coupling ports, as if the close() method had been called. 1.93 + if (this._closed) { 1.94 + if (DEBUG) debug("close() has been called. Don't need to close again."); 1.95 + return; 1.96 + } 1.97 + 1.98 + this.close(); 1.99 + }, 1.100 + 1.101 + postMessage: function(aMessage) { 1.102 + if (DEBUG) debug("Calling postMessage()."); 1.103 + 1.104 + if (this._closed) { 1.105 + if (DEBUG) debug("close() has been called. Cannot post message."); 1.106 + return; 1.107 + } 1.108 + 1.109 + cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage", 1.110 + { messagePortID: this._messagePortID, 1.111 + manifestURL: this._manifestURL, 1.112 + message: aMessage }); 1.113 + }, 1.114 + 1.115 + start: function() { 1.116 + // Begin dispatching messages received on the port. 1.117 + if (DEBUG) debug("Calling start()."); 1.118 + 1.119 + if (this._closed) { 1.120 + if (DEBUG) debug("close() has been called. Cannot call start()."); 1.121 + return; 1.122 + } 1.123 + 1.124 + if (this._started) { 1.125 + if (DEBUG) debug("start() has been called. Don't need to start again."); 1.126 + return; 1.127 + } 1.128 + 1.129 + // When a port's port message queue is enabled, the event loop must use it 1.130 + // as one of its task sources. 1.131 + this._started = true; 1.132 + while (this._messageQueue.length) { 1.133 + let message = this._messageQueue.shift(); 1.134 + this._dispatchMessage(message); 1.135 + } 1.136 + }, 1.137 + 1.138 + close: function() { 1.139 + // Disconnecting the port, so that it is no longer active. 1.140 + if (DEBUG) debug("Calling close()."); 1.141 + 1.142 + if (this._closed) { 1.143 + if (DEBUG) debug("close() has been called. Don't need to close again."); 1.144 + return; 1.145 + } 1.146 + 1.147 + this._closed = true; 1.148 + this._messageQueue.length = 0; 1.149 + 1.150 + // When this method called on a local port that is entangled with another 1.151 + // port, must cause the user agent to disentangle the coupling ports. 1.152 + cpmm.sendAsyncMessage("InterAppMessagePort:Unregister", 1.153 + { messagePortID: this._messagePortID, 1.154 + manifestURL: this._manifestURL }); 1.155 + }, 1.156 + 1.157 + get onmessage() { 1.158 + if (DEBUG) debug("Getting onmessage handler."); 1.159 + 1.160 + return this.__DOM_IMPL__.getEventHandler("onmessage"); 1.161 + }, 1.162 + 1.163 + set onmessage(aHandler) { 1.164 + if (DEBUG) debug("Setting onmessage handler."); 1.165 + 1.166 + this.__DOM_IMPL__.setEventHandler("onmessage", aHandler); 1.167 + 1.168 + // The first time a MessagePort object's onmessage IDL attribute is set, 1.169 + // the port's message queue must be enabled, as if the start() method had 1.170 + // been called. 1.171 + if (this._started) { 1.172 + if (DEBUG) debug("start() has been called. Don't need to start again."); 1.173 + return; 1.174 + } 1.175 + 1.176 + this.start(); 1.177 + }, 1.178 + 1.179 + _dispatchMessage: function _dispatchMessage(aMessage) { 1.180 + let wrappedMessage = Cu.cloneInto(aMessage, this._window); 1.181 + if (DEBUG) { 1.182 + debug("_dispatchMessage: wrappedMessage: " + 1.183 + JSON.stringify(wrappedMessage)); 1.184 + } 1.185 + 1.186 + let event = new this._window 1.187 + .MozInterAppMessageEvent("message", 1.188 + { data: wrappedMessage }); 1.189 + this.__DOM_IMPL__.dispatchEvent(event); 1.190 + }, 1.191 + 1.192 + receiveMessage: function(aMessage) { 1.193 + if (DEBUG) debug("receiveMessage: name: " + aMessage.name); 1.194 + 1.195 + let message = aMessage.json; 1.196 + if (message.manifestURL != this._manifestURL || 1.197 + message.pageURL != this._pageURL || 1.198 + message.messagePortID != this._messagePortID) { 1.199 + if (DEBUG) debug("The message doesn't belong to this page. Returning."); 1.200 + return; 1.201 + } 1.202 + 1.203 + switch (aMessage.name) { 1.204 + case "InterAppMessagePort:OnMessage": 1.205 + if (this._closed) { 1.206 + if (DEBUG) debug("close() has been called. Drop the message."); 1.207 + return; 1.208 + } 1.209 + 1.210 + if (!this._started) { 1.211 + if (DEBUG) debug("Not yet called start(). Queue up the message."); 1.212 + this._messageQueue.push(message.message); 1.213 + return; 1.214 + } 1.215 + 1.216 + this._dispatchMessage(message.message); 1.217 + break; 1.218 + 1.219 + default: 1.220 + if (DEBUG) debug("Error! Shouldn't fall into this case."); 1.221 + break; 1.222 + } 1.223 + } 1.224 +}; 1.225 + 1.226 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort]); 1.227 +