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; |
9 var lastError = require('lastError'); | 10 var lastError = require('lastError'); |
10 var logActivity = requireNative('activityLogger'); | 11 var logActivity = requireNative('activityLogger'); |
11 var logging = requireNative('logging'); | 12 var logging = requireNative('logging'); |
12 var messagingNatives = requireNative('messaging_natives'); | 13 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'); |
16 var messagingUtils = require('messaging_utils'); | 17 var messagingUtils = require('messaging_utils'); |
17 | 18 |
18 // The reserved channel name for the sendRequest/send(Native)Message APIs. | 19 // The reserved channel name for the sendRequest/send(Native)Message APIs. |
19 // Note: sendRequest is deprecated. | 20 // Note: sendRequest is deprecated. |
20 var kRequestChannel = "chrome.extension.sendRequest"; | 21 var kRequestChannel = "chrome.extension.sendRequest"; |
21 var kMessageChannel = "chrome.runtime.sendMessage"; | 22 var kMessageChannel = "chrome.runtime.sendMessage"; |
22 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; | 23 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; |
23 | 24 |
24 // Map of port IDs to port object. | 25 // Map of port IDs to port object. |
25 var ports = {}; | 26 var ports = {}; |
26 | 27 |
27 // Map of port IDs to unloadEvent listeners. Keep track of these to free the | 28 // Map of port IDs to unloadEvent listeners. Keep track of these to free the |
28 // unloadEvent listeners when ports are closed. | 29 // unloadEvent listeners when ports are closed. |
29 var portReleasers = {}; | 30 var portReleasers = {}; |
30 | 31 |
31 // Change even to odd and vice versa, to get the other side of a given | 32 // Change even to odd and vice versa, to get the other side of a given |
32 // channel. | 33 // channel. |
33 function getOppositePortId(portId) { return portId ^ 1; } | 34 function getOppositePortId(portId) { return portId ^ 1; } |
34 | 35 |
| 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 |
35 // Returns true if the specified port id is in this context. This is used by | 94 // Returns true if the specified port id is in this context. This is used by |
36 // the C++ to avoid creating the javascript message for all the contexts that | 95 // the C++ to avoid creating the javascript message for all the contexts that |
37 // don't care about a particular message. | 96 // don't care about a particular message. |
38 function hasPort(portId) { | 97 function hasPort(portId) { |
39 return portId in ports; | 98 return portId in ports; |
40 }; | 99 }; |
41 | 100 |
42 // Hidden port creation function. We don't want to expose an API that lets | 101 // Hidden port creation function. We don't want to expose an API that lets |
43 // people add arbitrary port IDs to the port list. | 102 // people add arbitrary port IDs to the port list. |
44 function createPort(portId, opt_name) { | 103 function createPort(portId, opt_name) { |
45 if (ports[portId]) | 104 if (ports[portId]) |
46 throw new Error("Port '" + portId + "' already exists."); | 105 throw new Error("Port '" + portId + "' already exists."); |
47 var port = new Port(portId, opt_name); | 106 var port = new Port(portId, opt_name); |
48 ports[portId] = port; | 107 ports[portId] = port; |
49 portReleasers[portId] = $Function.bind(messagingNatives.PortRelease, | 108 portReleasers[portId] = $Function.bind(messagingNatives.PortRelease, |
50 this, | 109 this, |
51 portId); | 110 portId); |
52 unloadEvent.addListener(portReleasers[portId]); | 111 unloadEvent.addListener(portReleasers[portId]); |
53 messagingNatives.PortAddRef(portId); | 112 messagingNatives.PortAddRef(portId); |
54 return port; | 113 return port; |
55 }; | 114 }; |
56 | 115 |
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 | |
65 // Helper function for dispatchOnRequest. | 116 // Helper function for dispatchOnRequest. |
66 function handleSendRequestError(isSendMessage, | 117 function handleSendRequestError(isSendMessage, |
67 responseCallbackPreserved, | 118 responseCallbackPreserved, |
68 sourceExtensionId, | 119 sourceExtensionId, |
69 targetExtensionId, | 120 targetExtensionId, |
70 sourceUrl) { | 121 sourceUrl) { |
71 var errorMsg = []; | 122 var errorMsg = []; |
72 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; | 123 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; |
73 if (isSendMessage && !responseCallbackPreserved) { | 124 if (isSendMessage && !responseCallbackPreserved) { |
74 $Array.push(errorMsg, | 125 $Array.push(errorMsg, |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 // If they didn't access the response callback, they're not | 193 // If they didn't access the response callback, they're not |
143 // going to send a response, so clean up the port immediately. | 194 // going to send a response, so clean up the port immediately. |
144 privates(port).impl.destroy_(); | 195 privates(port).impl.destroy_(); |
145 port = null; | 196 port = null; |
146 } | 197 } |
147 } | 198 } |
148 } | 199 } |
149 | 200 |
150 privates(port).impl.onDestroy_ = function() { | 201 privates(port).impl.onDestroy_ = function() { |
151 port.onMessage.removeListener(messageListener); | 202 port.onMessage.removeListener(messageListener); |
152 onPortDestroyed(port); | |
153 }; | 203 }; |
154 port.onMessage.addListener(messageListener); | 204 port.onMessage.addListener(messageListener); |
155 | 205 |
156 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; | 206 var eventName = isSendMessage ? "runtime.onMessage" : "extension.onRequest"; |
157 if (isExternal) | 207 if (isExternal) |
158 eventName += "External"; | 208 eventName += "External"; |
159 logActivity.LogEvent(targetExtensionId, | 209 logActivity.LogEvent(targetExtensionId, |
160 eventName, | 210 eventName, |
161 [sourceExtensionId, sourceUrl]); | 211 [sourceExtensionId, sourceUrl]); |
162 return true; | 212 return true; |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 try { | 351 try { |
302 responseCallback(response); | 352 responseCallback(response); |
303 } finally { | 353 } finally { |
304 port.disconnect(); | 354 port.disconnect(); |
305 } | 355 } |
306 } | 356 } |
307 | 357 |
308 privates(port).impl.onDestroy_ = function() { | 358 privates(port).impl.onDestroy_ = function() { |
309 port.onDisconnect.removeListener(disconnectListener); | 359 port.onDisconnect.removeListener(disconnectListener); |
310 port.onMessage.removeListener(messageListener); | 360 port.onMessage.removeListener(messageListener); |
311 onPortDestroyed(port); | |
312 }; | 361 }; |
313 port.onDisconnect.addListener(disconnectListener); | 362 port.onDisconnect.addListener(disconnectListener); |
314 port.onMessage.addListener(messageListener); | 363 port.onMessage.addListener(messageListener); |
315 }; | 364 }; |
316 | 365 |
317 function sendMessageUpdateArguments(functionName, hasOptionsArgument) { | 366 function sendMessageUpdateArguments(functionName, hasOptionsArgument) { |
318 // skip functionName and hasOptionsArgument | 367 // skip functionName and hasOptionsArgument |
319 var args = $Array.slice(arguments, 2); | 368 var args = $Array.slice(arguments, 2); |
320 var alignedArgs = messagingUtils.alignSendMessageArguments(args, | 369 var alignedArgs = messagingUtils.alignSendMessageArguments(args, |
321 hasOptionsArgument); | 370 hasOptionsArgument); |
322 if (!alignedArgs) | 371 if (!alignedArgs) |
323 throw new Error('Invalid arguments to ' + functionName + '.'); | 372 throw new Error('Invalid arguments to ' + functionName + '.'); |
324 return alignedArgs; | 373 return alignedArgs; |
325 } | 374 } |
326 | 375 |
| 376 var Port = utils.expose('Port', PortImpl, { functions: [ |
| 377 'disconnect', |
| 378 'postMessage' |
| 379 ], |
| 380 properties: [ |
| 381 'name', |
| 382 'onDisconnect', |
| 383 'onMessage' |
| 384 ] }); |
| 385 |
327 exports.kRequestChannel = kRequestChannel; | 386 exports.kRequestChannel = kRequestChannel; |
328 exports.kMessageChannel = kMessageChannel; | 387 exports.kMessageChannel = kMessageChannel; |
329 exports.kNativeMessageChannel = kNativeMessageChannel; | 388 exports.kNativeMessageChannel = kNativeMessageChannel; |
| 389 exports.Port = Port; |
330 exports.createPort = createPort; | 390 exports.createPort = createPort; |
331 exports.sendMessageImpl = sendMessageImpl; | 391 exports.sendMessageImpl = sendMessageImpl; |
332 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; | 392 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; |
333 | 393 |
334 // For C++ code to call. | 394 // For C++ code to call. |
335 exports.hasPort = hasPort; | 395 exports.hasPort = hasPort; |
336 exports.dispatchOnConnect = dispatchOnConnect; | 396 exports.dispatchOnConnect = dispatchOnConnect; |
337 exports.dispatchOnDisconnect = dispatchOnDisconnect; | 397 exports.dispatchOnDisconnect = dispatchOnDisconnect; |
338 exports.dispatchOnMessage = dispatchOnMessage; | 398 exports.dispatchOnMessage = dispatchOnMessage; |
OLD | NEW |