|
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/. */ |
|
4 |
|
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. |
|
11 |
|
12 "use strict"; |
|
13 |
|
14 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; |
|
15 |
|
16 Cu.import("resource://gre/modules/Services.jsm"); |
|
17 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
18 Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); |
|
19 |
|
20 const DEBUG = false; |
|
21 function debug(aMsg) { |
|
22 dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n"); |
|
23 } |
|
24 |
|
25 XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
|
26 "@mozilla.org/childprocessmessagemanager;1", |
|
27 "nsIMessageSender"); |
|
28 |
|
29 XPCOMUtils.defineLazyServiceGetter(this, "appsService", |
|
30 "@mozilla.org/AppsService;1", |
|
31 "nsIAppsService"); |
|
32 |
|
33 const kMessages = ["InterAppMessagePort:OnMessage"]; |
|
34 |
|
35 function InterAppMessagePort() { |
|
36 if (DEBUG) debug("InterAppMessagePort()"); |
|
37 }; |
|
38 |
|
39 InterAppMessagePort.prototype = { |
|
40 __proto__: DOMRequestIpcHelper.prototype, |
|
41 |
|
42 classDescription: "MozInterAppMessagePort", |
|
43 |
|
44 classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"), |
|
45 |
|
46 contractID: "@mozilla.org/dom/inter-app-message-port;1", |
|
47 |
|
48 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer, |
|
49 Ci.nsISupportsWeakReference, |
|
50 Ci.nsIObserver]), |
|
51 |
|
52 // Ci.nsIDOMGlobalPropertyInitializer implementation. |
|
53 init: function(aWindow) { |
|
54 if (DEBUG) debug("Calling init()."); |
|
55 |
|
56 this.initDOMRequestHelper(aWindow, kMessages); |
|
57 |
|
58 let principal = aWindow.document.nodePrincipal; |
|
59 this._manifestURL = appsService.getManifestURLByLocalId(principal.appId); |
|
60 this._pageURL = principal.URI.specIgnoringRef; |
|
61 |
|
62 // Remove query string. |
|
63 this._pageURL = this._pageURL.split("?")[0]; |
|
64 |
|
65 this._started = false; |
|
66 this._closed = false; |
|
67 this._messageQueue = []; |
|
68 }, |
|
69 |
|
70 // WebIDL implementation for constructor. |
|
71 __init: function(aMessagePortID) { |
|
72 if (DEBUG) { |
|
73 debug("Calling __init(): aMessagePortID: " + aMessagePortID); |
|
74 } |
|
75 |
|
76 this._messagePortID = aMessagePortID; |
|
77 |
|
78 cpmm.sendAsyncMessage("InterAppMessagePort:Register", |
|
79 { messagePortID: this._messagePortID, |
|
80 manifestURL: this._manifestURL, |
|
81 pageURL: this._pageURL }); |
|
82 }, |
|
83 |
|
84 // DOMRequestIpcHelper implementation. |
|
85 uninit: function() { |
|
86 if (DEBUG) debug("Calling uninit()."); |
|
87 |
|
88 // When the message port is uninitialized, we need to disentangle the |
|
89 // coupling ports, as if the close() method had been called. |
|
90 if (this._closed) { |
|
91 if (DEBUG) debug("close() has been called. Don't need to close again."); |
|
92 return; |
|
93 } |
|
94 |
|
95 this.close(); |
|
96 }, |
|
97 |
|
98 postMessage: function(aMessage) { |
|
99 if (DEBUG) debug("Calling postMessage()."); |
|
100 |
|
101 if (this._closed) { |
|
102 if (DEBUG) debug("close() has been called. Cannot post message."); |
|
103 return; |
|
104 } |
|
105 |
|
106 cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage", |
|
107 { messagePortID: this._messagePortID, |
|
108 manifestURL: this._manifestURL, |
|
109 message: aMessage }); |
|
110 }, |
|
111 |
|
112 start: function() { |
|
113 // Begin dispatching messages received on the port. |
|
114 if (DEBUG) debug("Calling start()."); |
|
115 |
|
116 if (this._closed) { |
|
117 if (DEBUG) debug("close() has been called. Cannot call start()."); |
|
118 return; |
|
119 } |
|
120 |
|
121 if (this._started) { |
|
122 if (DEBUG) debug("start() has been called. Don't need to start again."); |
|
123 return; |
|
124 } |
|
125 |
|
126 // When a port's port message queue is enabled, the event loop must use it |
|
127 // as one of its task sources. |
|
128 this._started = true; |
|
129 while (this._messageQueue.length) { |
|
130 let message = this._messageQueue.shift(); |
|
131 this._dispatchMessage(message); |
|
132 } |
|
133 }, |
|
134 |
|
135 close: function() { |
|
136 // Disconnecting the port, so that it is no longer active. |
|
137 if (DEBUG) debug("Calling close()."); |
|
138 |
|
139 if (this._closed) { |
|
140 if (DEBUG) debug("close() has been called. Don't need to close again."); |
|
141 return; |
|
142 } |
|
143 |
|
144 this._closed = true; |
|
145 this._messageQueue.length = 0; |
|
146 |
|
147 // When this method called on a local port that is entangled with another |
|
148 // port, must cause the user agent to disentangle the coupling ports. |
|
149 cpmm.sendAsyncMessage("InterAppMessagePort:Unregister", |
|
150 { messagePortID: this._messagePortID, |
|
151 manifestURL: this._manifestURL }); |
|
152 }, |
|
153 |
|
154 get onmessage() { |
|
155 if (DEBUG) debug("Getting onmessage handler."); |
|
156 |
|
157 return this.__DOM_IMPL__.getEventHandler("onmessage"); |
|
158 }, |
|
159 |
|
160 set onmessage(aHandler) { |
|
161 if (DEBUG) debug("Setting onmessage handler."); |
|
162 |
|
163 this.__DOM_IMPL__.setEventHandler("onmessage", aHandler); |
|
164 |
|
165 // The first time a MessagePort object's onmessage IDL attribute is set, |
|
166 // the port's message queue must be enabled, as if the start() method had |
|
167 // been called. |
|
168 if (this._started) { |
|
169 if (DEBUG) debug("start() has been called. Don't need to start again."); |
|
170 return; |
|
171 } |
|
172 |
|
173 this.start(); |
|
174 }, |
|
175 |
|
176 _dispatchMessage: function _dispatchMessage(aMessage) { |
|
177 let wrappedMessage = Cu.cloneInto(aMessage, this._window); |
|
178 if (DEBUG) { |
|
179 debug("_dispatchMessage: wrappedMessage: " + |
|
180 JSON.stringify(wrappedMessage)); |
|
181 } |
|
182 |
|
183 let event = new this._window |
|
184 .MozInterAppMessageEvent("message", |
|
185 { data: wrappedMessage }); |
|
186 this.__DOM_IMPL__.dispatchEvent(event); |
|
187 }, |
|
188 |
|
189 receiveMessage: function(aMessage) { |
|
190 if (DEBUG) debug("receiveMessage: name: " + aMessage.name); |
|
191 |
|
192 let message = aMessage.json; |
|
193 if (message.manifestURL != this._manifestURL || |
|
194 message.pageURL != this._pageURL || |
|
195 message.messagePortID != this._messagePortID) { |
|
196 if (DEBUG) debug("The message doesn't belong to this page. Returning."); |
|
197 return; |
|
198 } |
|
199 |
|
200 switch (aMessage.name) { |
|
201 case "InterAppMessagePort:OnMessage": |
|
202 if (this._closed) { |
|
203 if (DEBUG) debug("close() has been called. Drop the message."); |
|
204 return; |
|
205 } |
|
206 |
|
207 if (!this._started) { |
|
208 if (DEBUG) debug("Not yet called start(). Queue up the message."); |
|
209 this._messageQueue.push(message.message); |
|
210 return; |
|
211 } |
|
212 |
|
213 this._dispatchMessage(message.message); |
|
214 break; |
|
215 |
|
216 default: |
|
217 if (DEBUG) debug("Error! Shouldn't fall into this case."); |
|
218 break; |
|
219 } |
|
220 } |
|
221 }; |
|
222 |
|
223 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort]); |
|
224 |