Chromium Code Reviews| 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 |