OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // chrome.runtime.messaging API implementation. | 5 // chrome.runtime.messaging API implementation. |
6 | 6 |
7 // TODO(kalman): factor requiring chrome out of here. | 7 // TODO(kalman): factor requiring chrome out of here. |
8 var chrome = requireNative('chrome').GetChrome(); | 8 var chrome = requireNative('chrome').GetChrome(); |
9 var Event = require('event_bindings').Event; | |
10 var lastError = require('lastError'); | 9 var lastError = require('lastError'); |
11 var logActivity = requireNative('activityLogger'); | 10 var logActivity = requireNative('activityLogger'); |
12 var logging = requireNative('logging'); | 11 var logging = requireNative('logging'); |
13 var messagingNatives = requireNative('messaging_natives'); | 12 var messagingNatives = requireNative('messaging_natives'); |
13 var Port = require('port').Port; | |
14 var processNatives = requireNative('process'); | 14 var processNatives = requireNative('process'); |
15 var unloadEvent = require('unload_event'); | 15 var unloadEvent = require('unload_event'); |
16 var utils = require('utils'); | |
17 var messagingUtils = require('messaging_utils'); | 16 var messagingUtils = require('messaging_utils'); |
18 | 17 |
19 // The reserved channel name for the sendRequest/send(Native)Message APIs. | 18 // The reserved channel name for the sendRequest/send(Native)Message APIs. |
20 // Note: sendRequest is deprecated. | 19 // Note: sendRequest is deprecated. |
21 var kRequestChannel = "chrome.extension.sendRequest"; | 20 var kRequestChannel = "chrome.extension.sendRequest"; |
22 var kMessageChannel = "chrome.runtime.sendMessage"; | 21 var kMessageChannel = "chrome.runtime.sendMessage"; |
23 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; | 22 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; |
24 | 23 |
25 // Map of port IDs to port object. | 24 // Map of port IDs to port object. |
26 var ports = {}; | 25 var ports = {}; |
27 | 26 |
28 // Map of port IDs to unloadEvent listeners. Keep track of these to free the | 27 // Map of port IDs to unloadEvent listeners. Keep track of these to free the |
29 // unloadEvent listeners when ports are closed. | 28 // unloadEvent listeners when ports are closed. |
30 var portReleasers = {}; | 29 var portReleasers = {}; |
31 | 30 |
32 // Change even to odd and vice versa, to get the other side of a given | 31 // Change even to odd and vice versa, to get the other side of a given |
33 // channel. | 32 // channel. |
34 function getOppositePortId(portId) { return portId ^ 1; } | 33 function getOppositePortId(portId) { return portId ^ 1; } |
35 | 34 |
36 // Port object. Represents a connection to another script context through | |
37 // which messages can be passed. | |
38 function PortImpl(portId, opt_name) { | |
39 this.portId_ = portId; | |
40 this.name = opt_name; | |
41 | |
42 var portSchema = {name: 'port', $ref: 'runtime.Port'}; | |
43 var options = {unmanaged: true}; | |
44 this.onDisconnect = new Event(null, [portSchema], options); | |
45 this.onMessage = new Event( | |
46 null, | |
47 [{name: 'message', type: 'any', optional: true}, portSchema], | |
48 options); | |
49 this.onDestroy_ = null; | |
50 } | |
51 | |
52 // Sends a message asynchronously to the context on the other end of this | |
53 // port. | |
54 PortImpl.prototype.postMessage = function(msg) { | |
55 // JSON.stringify doesn't support a root object which is undefined. | |
56 if (msg === undefined) | |
57 msg = null; | |
58 msg = $JSON.stringify(msg); | |
59 if (msg === undefined) { | |
60 // JSON.stringify can fail with unserializable objects. Log an error and | |
61 // drop the message. | |
62 // | |
63 // TODO(kalman/mpcomplete): it would be better to do the same validation | |
64 // here that we do for runtime.sendMessage (and variants), i.e. throw an | |
65 // schema validation Error, but just maintain the old behaviour until | |
66 // there's a good reason not to (http://crbug.com/263077). | |
67 console.error('Illegal argument to Port.postMessage'); | |
68 return; | |
69 } | |
70 messagingNatives.PostMessage(this.portId_, msg); | |
71 }; | |
72 | |
73 // Disconnects the port from the other end. | |
74 PortImpl.prototype.disconnect = function() { | |
75 messagingNatives.CloseChannel(this.portId_, true); | |
76 this.destroy_(); | |
77 }; | |
78 | |
79 PortImpl.prototype.destroy_ = function() { | |
80 var portId = this.portId_; | |
81 | |
82 if (this.onDestroy_) | |
83 this.onDestroy_(); | |
84 privates(this.onDisconnect).impl.destroy_(); | |
85 privates(this.onMessage).impl.destroy_(); | |
86 | |
87 messagingNatives.PortRelease(portId); | |
88 unloadEvent.removeListener(portReleasers[portId]); | |
89 | |
90 delete ports[portId]; | |
91 delete portReleasers[portId]; | |
92 }; | |
93 | |
94 // Returns true if the specified port id is in this context. This is used by | 35 // Returns true if the specified port id is in this context. This is used by |
95 // the C++ to avoid creating the javascript message for all the contexts that | 36 // the C++ to avoid creating the javascript message for all the contexts that |
96 // don't care about a particular message. | 37 // don't care about a particular message. |
97 function hasPort(portId) { | 38 function hasPort(portId) { |
98 return portId in ports; | 39 return portId in ports; |
99 }; | 40 }; |
100 | 41 |
101 // Hidden port creation function. We don't want to expose an API that lets | 42 // Hidden port creation function. We don't want to expose an API that lets |
102 // people add arbitrary port IDs to the port list. | 43 // people add arbitrary port IDs to the port list. |
103 function createPort(portId, opt_name) { | 44 function createPort(portId, opt_name) { |
104 if (ports[portId]) | 45 if (ports[portId]) |
105 throw new Error("Port '" + portId + "' already exists."); | 46 throw new Error("Port '" + portId + "' already exists."); |
106 var port = new Port(portId, opt_name); | 47 var port = new Port(portId, opt_name); |
107 ports[portId] = port; | 48 ports[portId] = port; |
108 portReleasers[portId] = $Function.bind(messagingNatives.PortRelease, | 49 portReleasers[portId] = $Function.bind(messagingNatives.PortRelease, |
109 this, | 50 this, |
110 portId); | 51 portId); |
111 unloadEvent.addListener(portReleasers[portId]); | 52 unloadEvent.addListener(portReleasers[portId]); |
112 messagingNatives.PortAddRef(portId); | 53 messagingNatives.PortAddRef(portId); |
113 return port; | 54 return port; |
114 }; | 55 }; |
115 | 56 |
57 // Called when a Port is destroyed. Does general accounting cleanup. | |
58 function onPortDestroyed(port) { | |
59 var portId = privates(port).impl.portId_; | |
60 unloadEvent.removeListener(portReleasers[portId]); | |
61 delete ports[portId]; | |
62 delete portReleasers[portId]; | |
63 } | |
64 | |
116 // Helper function for dispatchOnRequest. | 65 // Helper function for dispatchOnRequest. |
117 function handleSendRequestError(isSendMessage, | 66 function handleSendRequestError(isSendMessage, |
118 responseCallbackPreserved, | 67 responseCallbackPreserved, |
119 sourceExtensionId, | 68 sourceExtensionId, |
120 targetExtensionId, | 69 targetExtensionId, |
121 sourceUrl) { | 70 sourceUrl) { |
122 var errorMsg = []; | 71 var errorMsg = []; |
123 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; | 72 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; |
124 if (isSendMessage && !responseCallbackPreserved) { | 73 if (isSendMessage && !responseCallbackPreserved) { |
125 $Array.push(errorMsg, | 74 $Array.push(errorMsg, |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
193 // If they didn't access the response callback, they're not | 142 // If they didn't access the response callback, they're not |
194 // going to send a response, so clean up the port immediately. | 143 // going to send a response, so clean up the port immediately. |
195 privates(port).impl.destroy_(); | 144 privates(port).impl.destroy_(); |
196 port = null; | 145 port = null; |
197 } | 146 } |
198 } | 147 } |
199 } | 148 } |
200 | 149 |
201 privates(port).impl.onDestroy_ = function() { | 150 privates(port).impl.onDestroy_ = function() { |
202 port.onMessage.removeListener(messageListener); | 151 port.onMessage.removeListener(messageListener); |
152 onPortDestroyed(port); | |
203 }; | 153 }; |
204 port.onMessage.addListener(messageListener); | 154 port.onMessage.addListener(messageListener); |
205 | 155 |
206 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; | 156 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; |
207 if (isExternal) | 157 if (isExternal) |
208 eventName += "External"; | 158 eventName += "External"; |
209 logActivity.LogEvent(targetExtensionId, | 159 logActivity.LogEvent(targetExtensionId, |
210 eventName, | 160 eventName, |
211 [sourceExtensionId, sourceUrl]); | 161 [sourceExtensionId, sourceUrl]); |
212 return true; | 162 return true; |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
348 } | 298 } |
349 | 299 |
350 function messageListener(response) { | 300 function messageListener(response) { |
351 try { | 301 try { |
352 responseCallback(response); | 302 responseCallback(response); |
353 } finally { | 303 } finally { |
354 port.disconnect(); | 304 port.disconnect(); |
355 } | 305 } |
356 } | 306 } |
357 | 307 |
358 privates(port).impl.onDestroy_ = function() { | 308 privates(port).impl.onDestroy_ = function() { |
Devlin
2015/04/17 00:14:52
I don't love this part, especially now that it's i
not at google - send to devlin
2015/04/17 00:16:34
You need to be careful, because Ports are exposed
Devlin
2015/04/17 00:25:52
Right, in my mind there's:
Private private: Nothin
not at google - send to devlin
2015/04/17 00:27:59
Heh, a distinction unique to these JS bindings.
A
| |
359 port.onDisconnect.removeListener(disconnectListener); | 309 port.onDisconnect.removeListener(disconnectListener); |
360 port.onMessage.removeListener(messageListener); | 310 port.onMessage.removeListener(messageListener); |
311 onPortDestroyed(port); | |
361 }; | 312 }; |
362 port.onDisconnect.addListener(disconnectListener); | 313 port.onDisconnect.addListener(disconnectListener); |
363 port.onMessage.addListener(messageListener); | 314 port.onMessage.addListener(messageListener); |
364 }; | 315 }; |
365 | 316 |
366 function sendMessageUpdateArguments(functionName, hasOptionsArgument) { | 317 function sendMessageUpdateArguments(functionName, hasOptionsArgument) { |
367 // skip functionName and hasOptionsArgument | 318 // skip functionName and hasOptionsArgument |
368 var args = $Array.slice(arguments, 2); | 319 var args = $Array.slice(arguments, 2); |
369 var alignedArgs = messagingUtils.alignSendMessageArguments(args, | 320 var alignedArgs = messagingUtils.alignSendMessageArguments(args, |
370 hasOptionsArgument); | 321 hasOptionsArgument); |
371 if (!alignedArgs) | 322 if (!alignedArgs) |
372 throw new Error('Invalid arguments to ' + functionName + '.'); | 323 throw new Error('Invalid arguments to ' + functionName + '.'); |
373 return alignedArgs; | 324 return alignedArgs; |
374 } | 325 } |
375 | 326 |
376 var Port = utils.expose('Port', PortImpl, { functions: [ | |
377 'disconnect', | |
378 'postMessage' | |
379 ], | |
380 properties: [ | |
381 'name', | |
382 'onDisconnect', | |
383 'onMessage' | |
384 ] }); | |
385 | |
386 exports.kRequestChannel = kRequestChannel; | 327 exports.kRequestChannel = kRequestChannel; |
387 exports.kMessageChannel = kMessageChannel; | 328 exports.kMessageChannel = kMessageChannel; |
388 exports.kNativeMessageChannel = kNativeMessageChannel; | 329 exports.kNativeMessageChannel = kNativeMessageChannel; |
389 exports.Port = Port; | |
390 exports.createPort = createPort; | 330 exports.createPort = createPort; |
391 exports.sendMessageImpl = sendMessageImpl; | 331 exports.sendMessageImpl = sendMessageImpl; |
392 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; | 332 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; |
393 | 333 |
394 // For C++ code to call. | 334 // For C++ code to call. |
395 exports.hasPort = hasPort; | 335 exports.hasPort = hasPort; |
396 exports.dispatchOnConnect = dispatchOnConnect; | 336 exports.dispatchOnConnect = dispatchOnConnect; |
397 exports.dispatchOnDisconnect = dispatchOnDisconnect; | 337 exports.dispatchOnDisconnect = dispatchOnDisconnect; |
398 exports.dispatchOnMessage = dispatchOnMessage; | 338 exports.dispatchOnMessage = dispatchOnMessage; |
OLD | NEW |