Bumping manifests a=b2g-bump
[gecko.git] / dom / apps / InterAppMessagePort.js
blob7d3ec9ba7a9c1f31e0aba26bd2b535a9aae45b65
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // TODO Bug 907060 Per off-line discussion, after the MessagePort is done
6 // at Bug 643325, we will start to refactorize the common logic of both
7 // Inter-App Communication and Shared Worker. For now, we hope to design an
8 // MozInterAppMessagePort to meet the timeline, which still follows exactly
9 // the same interface and semantic as the MessagePort is. In the future,
10 // we can then align it back to MessagePort with backward compatibility.
12 "use strict";
14 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
16 Cu.import("resource://gre/modules/Services.jsm");
17 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
18 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
20 const DEBUG = false;
21 function debug(aMsg) {
22   dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n");
25 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
26                                    "@mozilla.org/childprocessmessagemanager;1",
27                                    "nsIMessageSender");
29 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
30                                    "@mozilla.org/AppsService;1",
31                                    "nsIAppsService");
33 const kMessages = ["InterAppMessagePort:OnMessage",
34                    "InterAppMessagePort:Shutdown"];
36 function InterAppMessagePort() {
37   if (DEBUG) debug("InterAppMessagePort()");
40 InterAppMessagePort.prototype = {
41   __proto__: DOMRequestIpcHelper.prototype,
43   classDescription: "MozInterAppMessagePort",
45   classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"),
47   contractID: "@mozilla.org/dom/inter-app-message-port;1",
49   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
50                                          Ci.nsISupportsWeakReference,
51                                          Ci.nsIObserver]),
53   // Ci.nsIDOMGlobalPropertyInitializer implementation.
54   init: function(aWindow) {
55     if (DEBUG) debug("Calling init().");
57     this.initDOMRequestHelper(aWindow, kMessages);
59     let principal = aWindow.document.nodePrincipal;
60     this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
61     this._pageURL = principal.URI.specIgnoringRef;
63     // Remove query string.
64     this._pageURL = this._pageURL.split("?")[0];
66     this._started = false;
67     this._closed = false;
68     this._messageQueue = [];
69   },
71   // WebIDL implementation for constructor.
72   __init: function(aMessagePortID) {
73     if (DEBUG) {
74       debug("Calling __init(): aMessagePortID: " + aMessagePortID);
75     }
77     this._messagePortID = aMessagePortID;
79     cpmm.sendAsyncMessage("InterAppMessagePort:Register",
80                           { messagePortID: this._messagePortID,
81                             manifestURL: this._manifestURL,
82                             pageURL: this._pageURL });
83   },
85   // DOMRequestIpcHelper implementation.
86   uninit: function() {
87     if (DEBUG) debug("Calling uninit().");
89     // When the message port is uninitialized, we need to disentangle the
90     // coupling ports, as if the close() method had been called.
91     if (this._closed) {
92       if (DEBUG) debug("close() has been called. Don't need to close again.");
93       return;
94     }
96     this.close();
97   },
99   postMessage: function(aMessage) {
100     if (DEBUG) debug("Calling postMessage().");
102     if (this._closed) {
103       if (DEBUG) debug("close() has been called. Cannot post message.");
104       return;
105     }
107     cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage",
108                           { messagePortID: this._messagePortID,
109                             manifestURL: this._manifestURL,
110                             message: aMessage });
111   },
113   start: function() {
114     // Begin dispatching messages received on the port.
115     if (DEBUG) debug("Calling start().");
117     if (this._closed) {
118       if (DEBUG) debug("close() has been called. Cannot call start().");
119       return;
120     }
122     if (this._started) {
123       if (DEBUG) debug("start() has been called. Don't need to start again.");
124       return;
125     }
127     // When a port's port message queue is enabled, the event loop must use it
128     // as one of its task sources.
129     this._started = true;
130     while (this._messageQueue.length) {
131       let message = this._messageQueue.shift();
132       this._dispatchMessage(message);
133     }
134   },
136   close: function() {
137     // Disconnecting the port, so that it is no longer active.
138     if (DEBUG) debug("Calling close().");
140     if (this._closed) {
141       if (DEBUG) debug("close() has been called. Don't need to close again.");
142       return;
143     }
145     this._closed = true;
146     this._messageQueue.length = 0;
148     // When this method called on a local port that is entangled with another
149     // port, must cause the user agent to disentangle the coupling ports.
150     cpmm.sendAsyncMessage("InterAppMessagePort:Unregister",
151                           { messagePortID: this._messagePortID,
152                             manifestURL: this._manifestURL });
154     this.removeMessageListeners(kMessages);
155   },
157   get onmessage() {
158     if (DEBUG) debug("Getting onmessage handler.");
160     return this.__DOM_IMPL__.getEventHandler("onmessage");
161   },
163   set onmessage(aHandler) {
164     if (DEBUG) debug("Setting onmessage handler.");
166     this.__DOM_IMPL__.setEventHandler("onmessage", aHandler);
168     // The first time a MessagePort object's onmessage IDL attribute is set,
169     // the port's message queue must be enabled, as if the start() method had
170     // been called.
171     if (this._started) {
172       if (DEBUG) debug("start() has been called. Don't need to start again.");
173       return;
174     }
176     this.start();
177   },
179   _dispatchMessage: function _dispatchMessage(aMessage) {
180     let wrappedMessage = Cu.cloneInto(aMessage, this._window);
181     if (DEBUG) {
182       debug("_dispatchMessage: wrappedMessage: " +
183             JSON.stringify(wrappedMessage));
184     }
186     let event = new this._window
187                     .MozInterAppMessageEvent("message",
188                                              { data: wrappedMessage });
189     this.__DOM_IMPL__.dispatchEvent(event);
190   },
192   receiveMessage: function(aMessage) {
193     if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
195     let message = aMessage.json;
196     if (message.manifestURL != this._manifestURL ||
197         message.pageURL != this._pageURL ||
198         message.messagePortID != this._messagePortID) {
199       if (DEBUG) debug("The message doesn't belong to this page. Returning. " +
200                        uneval(message));
201       return;
202     }
204     switch (aMessage.name) {
205       case "InterAppMessagePort:OnMessage":
206         if (this._closed) {
207           if (DEBUG) debug("close() has been called. Drop the message.");
208           return;
209         }
211         if (!this._started) {
212           if (DEBUG) debug("Not yet called start(). Queue up the message.");
213           this._messageQueue.push(message.message);
214           return;
215         }
217         this._dispatchMessage(message.message);
218         break;
220       case "InterAppMessagePort:Shutdown":
221         this.close();
222         break;
223       default:
224         if (DEBUG) debug("Error! Shouldn't fall into this case.");
225         break;
226     }
227   }
230 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort]);