| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // This contains unprivileged javascript APIs for extensions and apps. It | 5 // This contains unprivileged javascript APIs for extensions and apps. It |
| 6 // can be loaded by any extension-related context, such as content scripts or | 6 // can be loaded by any extension-related context, such as content scripts or |
| 7 // background pages. See user_script_slave.cc for script that is loaded by | 7 // background pages. See user_script_slave.cc for script that is loaded by |
| 8 // content scripts only. | 8 // content scripts only. |
| 9 | 9 |
| 10 require('json_schema'); | 10 var chrome = requireNative('chrome').GetChrome(); |
| 11 var json = require('json'); | 11 var Event = require('event_bindings').Event; |
| 12 var lastError = require('lastError'); | 12 var lastError = require('lastError'); |
| 13 var miscNatives = requireNative('miscellaneous_bindings'); | 13 var logActivity = requireNative('activityLogger'); |
| 14 var chrome = requireNative('chrome').GetChrome(); | 14 var miscNatives = requireNative('miscellaneous_bindings_natives'); |
| 15 var CloseChannel = miscNatives.CloseChannel; | 15 var onUnload = require('on_unload'); |
| 16 var PortAddRef = miscNatives.PortAddRef; | 16 var processNatives = requireNative('process'); |
| 17 var PortRelease = miscNatives.PortRelease; | |
| 18 var PostMessage = miscNatives.PostMessage; | |
| 19 var BindToGC = miscNatives.BindToGC; | |
| 20 | 17 |
| 21 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | 18 // The reserved channel name for the sendRequest/send(Native)Message APIs. |
| 22 | |
| 23 var processNatives = requireNative('process'); | |
| 24 var manifestVersion = processNatives.GetManifestVersion(); | |
| 25 var extensionId = processNatives.GetExtensionId(); | |
| 26 | |
| 27 var logActivity = requireNative('activityLogger'); | |
| 28 | |
| 29 // The reserved channel name for the sendRequest/sendMessage APIs. | |
| 30 // Note: sendRequest is deprecated. | 19 // Note: sendRequest is deprecated. |
| 31 chromeHidden.kRequestChannel = "chrome.extension.sendRequest"; | 20 var kRequestChannel = "chrome.extension.sendRequest"; |
| 32 chromeHidden.kMessageChannel = "chrome.runtime.sendMessage"; | 21 var kMessageChannel = "chrome.runtime.sendMessage"; |
| 33 chromeHidden.kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; | 22 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; |
| 34 | 23 |
| 35 // Map of port IDs to port object. | 24 // Map of port IDs to port object. |
| 36 var ports = {}; | 25 var ports = {}; |
| 37 | 26 |
| 38 // Map of port IDs to chromeHidden.onUnload listeners. Keep track of these | 27 // Map of port IDs to onUnload listeners. Keep track of these to free the |
| 39 // to free the onUnload listeners when ports are closed. | 28 // onUnload listeners when ports are closed. |
| 40 var portReleasers = {}; | 29 var portReleasers = {}; |
| 41 | 30 |
| 42 // 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 |
| 43 // channel. | 32 // channel. |
| 44 function getOppositePortId(portId) { return portId ^ 1; } | 33 function getOppositePortId(portId) { return portId ^ 1; } |
| 45 | 34 |
| 46 // Port object. Represents a connection to another script context through | 35 // Port object. Represents a connection to another script context through |
| 47 // which messages can be passed. | 36 // which messages can be passed. |
| 48 function PortImpl(portId, opt_name) { | 37 function Port(portId, opt_name) { |
| 49 this.portId_ = portId; | 38 this.portId_ = portId; |
| 50 this.name = opt_name; | 39 this.name = opt_name; |
| 51 this.onDisconnect = new chrome.Event(); | 40 this.onDisconnect = new Event(); |
| 52 this.onMessage = new chrome.Event(); | 41 this.onMessage = new Event(); |
| 53 } | 42 } |
| 54 | 43 |
| 55 // Sends a message asynchronously to the context on the other end of this | 44 // Sends a message asynchronously to the context on the other end of this |
| 56 // port. | 45 // port. |
| 57 PortImpl.prototype.postMessage = function(msg) { | 46 Port.prototype.postMessage = function(msg) { |
| 58 // json.stringify doesn't support a root object which is undefined. | 47 miscNatives.PostMessage(this.portId_, msg); |
| 59 if (msg === undefined) | |
| 60 msg = null; | |
| 61 PostMessage(this.portId_, json.stringify(msg)); | |
| 62 }; | 48 }; |
| 63 | 49 |
| 64 // Disconnects the port from the other end. | 50 // Disconnects the port from the other end. |
| 65 PortImpl.prototype.disconnect = function() { | 51 Port.prototype.disconnect = function() { |
| 66 CloseChannel(this.portId_, true); | 52 miscNatives.CloseChannel(this.portId_, true); |
| 67 this.destroy_(); | 53 this.destroy_(); |
| 68 }; | 54 }; |
| 69 | 55 |
| 70 PortImpl.prototype.destroy_ = function() { | 56 Port.prototype.destroy_ = function() { |
| 71 var portId = this.portId_; | 57 var portId = this.portId_; |
| 72 | 58 |
| 73 this.onDisconnect.destroy_(); | 59 this.onDisconnect.destroy_(); |
| 74 this.onMessage.destroy_(); | 60 this.onMessage.destroy_(); |
| 75 | 61 |
| 76 PortRelease(portId); | 62 miscNatives.PortRelease(portId); |
| 77 chromeHidden.onUnload.removeListener(portReleasers[portId]); | 63 onUnload.removeListener(portReleasers[portId]); |
| 78 | 64 |
| 79 delete ports[portId]; | 65 delete ports[portId]; |
| 80 delete portReleasers[portId]; | 66 delete portReleasers[portId]; |
| 81 }; | 67 }; |
| 82 | 68 |
| 83 chromeHidden.Port = {}; | |
| 84 | |
| 85 // Returns true if the specified port id is in this context. This is used by | 69 // Returns true if the specified port id is in this context. This is used by |
| 86 // the C++ to avoid creating the javascript message for all the contexts that | 70 // the C++ to avoid creating the javascript message for all the contexts that |
| 87 // don't care about a particular message. | 71 // don't care about a particular message. |
| 88 chromeHidden.Port.hasPort = function(portId) { | 72 function hasPort(portId) { |
| 89 return portId in ports; | 73 return portId in ports; |
| 90 }; | 74 }; |
| 91 | 75 |
| 92 // Hidden port creation function. We don't want to expose an API that lets | 76 // Hidden port creation function. We don't want to expose an API that lets |
| 93 // people add arbitrary port IDs to the port list. | 77 // people add arbitrary port IDs to the port list. |
| 94 chromeHidden.Port.createPort = function(portId, opt_name) { | 78 function createPort(portId, opt_name) { |
| 95 if (ports[portId]) { | 79 if (ports[portId]) |
| 96 throw new Error("Port '" + portId + "' already exists."); | 80 throw new Error("Port '" + portId + "' already exists."); |
| 97 } | 81 var port = new Port(portId, opt_name); |
| 98 var port = new PortImpl(portId, opt_name); | |
| 99 ports[portId] = port; | 82 ports[portId] = port; |
| 100 portReleasers[portId] = PortRelease.bind(this, portId); | 83 portReleasers[portId] = miscNatives.PortRelease.bind(this, portId); |
| 101 chromeHidden.onUnload.addListener(portReleasers[portId]); | 84 onUnload.addListener(portReleasers[portId]); |
| 102 | 85 miscNatives.PortAddRef(portId); |
| 103 PortAddRef(portId); | |
| 104 return port; | 86 return port; |
| 105 }; | 87 }; |
| 106 | 88 |
| 107 // Helper function for dispatchOnRequest. | 89 // Helper function for dispatchOnRequest. |
| 108 function handleSendRequestError(isSendMessage, | 90 function handleSendRequestError(isSendMessage, |
| 109 responseCallbackPreserved, | 91 responseCallbackPreserved, |
| 110 sourceExtensionId, | 92 sourceExtensionId, |
| 111 targetExtensionId, | 93 targetExtensionId, |
| 112 sourceUrl) { | 94 sourceUrl) { |
| 113 var errorMsg = []; | 95 var errorMsg = []; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 126 errorMsg.push("for extension " + targetExtensionId); | 108 errorMsg.push("for extension " + targetExtensionId); |
| 127 if (sourceUrl != "") | 109 if (sourceUrl != "") |
| 128 errorMsg.push("for URL " + sourceUrl); | 110 errorMsg.push("for URL " + sourceUrl); |
| 129 lastError.set(eventName, errorMsg.join(" ") + ").", null, chrome); | 111 lastError.set(eventName, errorMsg.join(" ") + ").", null, chrome); |
| 130 } | 112 } |
| 131 | 113 |
| 132 // Helper function for dispatchOnConnect | 114 // Helper function for dispatchOnConnect |
| 133 function dispatchOnRequest(portId, channelName, sender, | 115 function dispatchOnRequest(portId, channelName, sender, |
| 134 sourceExtensionId, targetExtensionId, sourceUrl, | 116 sourceExtensionId, targetExtensionId, sourceUrl, |
| 135 isExternal) { | 117 isExternal) { |
| 136 var isSendMessage = channelName == chromeHidden.kMessageChannel; | 118 var isSendMessage = channelName == kMessageChannel; |
| 137 var requestEvent = (isSendMessage ? | 119 var requestEvent = (isSendMessage ? |
| 138 (isExternal ? | 120 (isExternal ? |
| 139 chrome.runtime.onMessageExternal : chrome.runtime.onMessage) : | 121 chrome.runtime.onMessageExternal : chrome.runtime.onMessage) : |
| 140 (isExternal ? | 122 (isExternal ? |
| 141 chrome.extension.onRequestExternal : chrome.extension.onRequest)); | 123 chrome.extension.onRequestExternal : chrome.extension.onRequest)); |
| 142 if (requestEvent.hasListeners()) { | 124 if (!requestEvent.hasListeners()) |
| 143 var port = chromeHidden.Port.createPort(portId, channelName); | 125 return false; |
| 144 port.onMessage.addListener(function(request) { | 126 var port = createPort(portId, channelName); |
| 145 var responseCallbackPreserved = false; | 127 port.onMessage.addListener(function(request) { |
| 146 var responseCallback = function(response) { | 128 var responseCallbackPreserved = false; |
| 147 if (port) { | 129 var responseCallback = function(response) { |
| 148 port.postMessage(response); | 130 if (port) { |
| 149 port.destroy_(); | 131 port.postMessage(response); |
| 150 port = null; | 132 port.destroy_(); |
| 151 } else { | 133 port = null; |
| 152 // We nulled out port when sending the response, and now the page | |
| 153 // is trying to send another response for the same request. | |
| 154 handleSendRequestError(isSendMessage, responseCallbackPreserved, | |
| 155 sourceExtensionId, targetExtensionId); | |
| 156 } | |
| 157 }; | |
| 158 // In case the extension never invokes the responseCallback, and also | |
| 159 // doesn't keep a reference to it, we need to clean up the port. Do | |
| 160 // so by attaching to the garbage collection of the responseCallback | |
| 161 // using some native hackery. | |
| 162 BindToGC(responseCallback, function() { | |
| 163 if (port) { | |
| 164 port.destroy_(); | |
| 165 port = null; | |
| 166 } | |
| 167 }); | |
| 168 if (!isSendMessage) { | |
| 169 requestEvent.dispatch(request, sender, responseCallback); | |
| 170 } else { | 134 } else { |
| 171 var rv = requestEvent.dispatch(request, sender, responseCallback); | 135 // We nulled out port when sending the response, and now the page |
| 172 responseCallbackPreserved = | 136 // is trying to send another response for the same request. |
| 173 rv && rv.results && rv.results.indexOf(true) > -1; | 137 handleSendRequestError(isSendMessage, responseCallbackPreserved, |
| 174 if (!responseCallbackPreserved && port) { | 138 sourceExtensionId, targetExtensionId); |
| 175 // If they didn't access the response callback, they're not | 139 } |
| 176 // going to send a response, so clean up the port immediately. | 140 }; |
| 177 port.destroy_(); | 141 // In case the extension never invokes the responseCallback, and also |
| 178 port = null; | 142 // doesn't keep a reference to it, we need to clean up the port. Do |
| 179 } | 143 // so by attaching to the garbage collection of the responseCallback |
| 144 // using some native hackery. |
| 145 miscNatives.BindToGC(responseCallback, function() { |
| 146 if (port) { |
| 147 port.destroy_(); |
| 148 port = null; |
| 180 } | 149 } |
| 181 }); | 150 }); |
| 182 var eventName = (isSendMessage ? | 151 if (!isSendMessage) { |
| 183 (isExternal ? | 152 requestEvent.dispatch(request, sender, responseCallback); |
| 184 "runtime.onMessageExternal" : "runtime.onMessage") : | 153 } else { |
| 185 (isExternal ? | 154 var rv = requestEvent.dispatch(request, sender, responseCallback); |
| 186 "extension.onRequestExternal" : "extension.onRequest")); | 155 responseCallbackPreserved = |
| 187 logActivity.LogEvent(targetExtensionId, | 156 rv && rv.results && rv.results.indexOf(true) > -1; |
| 188 eventName, | 157 if (!responseCallbackPreserved && port) { |
| 189 [sourceExtensionId, sourceUrl]); | 158 // If they didn't access the response callback, they're not |
| 190 return true; | 159 // going to send a response, so clean up the port immediately. |
| 191 } | 160 port.destroy_(); |
| 192 return false; | 161 port = null; |
| 162 } |
| 163 } |
| 164 }); |
| 165 var eventName = (isSendMessage ? |
| 166 (isExternal ? |
| 167 "runtime.onMessageExternal" : "runtime.onMessage") : |
| 168 (isExternal ? |
| 169 "extension.onRequestExternal" : "extension.onRequest")); |
| 170 logActivity.LogEvent(targetExtensionId, |
| 171 eventName, |
| 172 [sourceExtensionId, sourceUrl]); |
| 173 return true; |
| 193 } | 174 } |
| 194 | 175 |
| 195 // Called by native code when a channel has been opened to this context. | 176 // Called by native code when a channel has been opened to this context. |
| 196 chromeHidden.Port.dispatchOnConnect = function(portId, | 177 function dispatchOnConnect(portId, |
| 197 channelName, | 178 channelName, |
| 198 sourceTab, | 179 sourceTab, |
| 199 sourceExtensionId, | 180 sourceExtensionId, |
| 200 targetExtensionId, | 181 targetExtensionId, |
| 201 sourceUrl) { | 182 sourceUrl) { |
| 202 // Only create a new Port if someone is actually listening for a connection. | 183 // Only create a new Port if someone is actually listening for a connection. |
| 203 // In addition to being an optimization, this also fixes a bug where if 2 | 184 // In addition to being an optimization, this also fixes a bug where if 2 |
| 204 // channels were opened to and from the same process, closing one would | 185 // channels were opened to and from the same process, closing one would |
| 205 // close both. | 186 // close both. |
| 187 var extensionId = processNatives.GetExtensionId(); |
| 206 if (targetExtensionId != extensionId) | 188 if (targetExtensionId != extensionId) |
| 207 return false; // not for us | 189 return false; // not for us |
| 190 |
| 208 if (ports[getOppositePortId(portId)]) | 191 if (ports[getOppositePortId(portId)]) |
| 209 return false; // this channel was opened by us, so ignore it | 192 return false; // this channel was opened by us, so ignore it |
| 210 | 193 |
| 211 // Determine whether this is coming from another extension, so we can use | 194 // Determine whether this is coming from another extension, so we can use |
| 212 // the right event. | 195 // the right event. |
| 213 var isExternal = sourceExtensionId != extensionId; | 196 var isExternal = sourceExtensionId != extensionId; |
| 214 | 197 |
| 215 var sender = {id: sourceExtensionId}; | 198 var sender = {id: sourceExtensionId}; |
| 216 if (sourceUrl) | 199 if (sourceUrl) |
| 217 sender.url = sourceUrl; | 200 sender.url = sourceUrl; |
| 218 if (sourceTab) | 201 if (sourceTab) |
| 219 sender.tab = sourceTab; | 202 sender.tab = sourceTab; |
| 220 | 203 |
| 221 // Special case for sendRequest/onRequest and sendMessage/onMessage. | 204 // Special case for sendRequest/onRequest and sendMessage/onMessage. |
| 222 if (channelName == chromeHidden.kRequestChannel || | 205 if (channelName == kRequestChannel || channelName == kMessageChannel) { |
| 223 channelName == chromeHidden.kMessageChannel) { | |
| 224 return dispatchOnRequest(portId, channelName, sender, | 206 return dispatchOnRequest(portId, channelName, sender, |
| 225 sourceExtensionId, targetExtensionId, sourceUrl, | 207 sourceExtensionId, targetExtensionId, sourceUrl, |
| 226 isExternal); | 208 isExternal); |
| 227 } | 209 } |
| 228 | 210 |
| 229 var connectEvent = (isExternal ? | 211 var connectEvent = (isExternal ? |
| 230 chrome.runtime.onConnectExternal : chrome.runtime.onConnect); | 212 chrome.runtime.onConnectExternal : chrome.runtime.onConnect); |
| 231 if (connectEvent.hasListeners()) { | 213 if (!connectEvent) |
| 232 var port = chromeHidden.Port.createPort(portId, channelName); | 214 return false; |
| 233 port.sender = sender; | 215 if (!connectEvent.hasListeners()) |
| 234 if (manifestVersion < 2) | 216 return false; |
| 235 port.tab = port.sender.tab; | |
| 236 | 217 |
| 237 var eventName = (isExternal ? | 218 var port = createPort(portId, channelName); |
| 238 "runtime.onConnectExternal" : "runtime.onConnect"); | 219 port.sender = sender; |
| 239 connectEvent.dispatch(port); | 220 if (processNatives.manifestVersion < 2) |
| 240 logActivity.LogEvent(targetExtensionId, | 221 port.tab = port.sender.tab; |
| 241 eventName, | 222 |
| 242 [sourceExtensionId]); | 223 var eventName = (isExternal ? |
| 243 return true; | 224 "runtime.onConnectExternal" : "runtime.onConnect"); |
| 244 } | 225 connectEvent.dispatch(port); |
| 245 return false; | 226 logActivity.LogEvent(targetExtensionId, |
| 227 eventName, |
| 228 [sourceExtensionId]); |
| 229 return true; |
| 246 }; | 230 }; |
| 247 | 231 |
| 248 // Called by native code when a channel has been closed. | 232 // Called by native code when a channel has been closed. |
| 249 chromeHidden.Port.dispatchOnDisconnect = function( | 233 function dispatchOnDisconnect(portId, errorMessage) { |
| 250 portId, errorMessage) { | |
| 251 var port = ports[portId]; | 234 var port = ports[portId]; |
| 252 if (port) { | 235 if (port) { |
| 253 // Update the renderer's port bookkeeping, without notifying the browser. | 236 // Update the renderer's port bookkeeping, without notifying the browser. |
| 254 CloseChannel(portId, false); | 237 miscNatives.CloseChannel(portId, false); |
| 255 if (errorMessage) | 238 if (errorMessage) |
| 256 lastError.set('Port', errorMessage, null, chrome); | 239 lastError.set('Port', errorMessage, null, chrome); |
| 257 try { | 240 try { |
| 258 port.onDisconnect.dispatch(port); | 241 port.onDisconnect.dispatch(port); |
| 259 } finally { | 242 } finally { |
| 260 port.destroy_(); | 243 port.destroy_(); |
| 261 lastError.clear(chrome); | 244 lastError.clear(chrome); |
| 262 } | 245 } |
| 263 } | 246 } |
| 264 }; | 247 }; |
| 265 | 248 |
| 266 // Called by native code when a message has been sent to the given port. | 249 // Called by native code when a message has been sent to the given port. |
| 267 chromeHidden.Port.dispatchOnMessage = function(msg, portId) { | 250 function dispatchOnMessage(msg, portId) { |
| 268 var port = ports[portId]; | 251 var port = ports[portId]; |
| 269 if (port) { | 252 if (port) |
| 270 if (msg) { | |
| 271 msg = json.parse(msg); | |
| 272 } | |
| 273 port.onMessage.dispatch(msg, port); | 253 port.onMessage.dispatch(msg, port); |
| 274 } | |
| 275 }; | 254 }; |
| 276 | 255 |
| 277 // Shared implementation used by tabs.sendMessage and runtime.sendMessage. | 256 // Shared implementation used by tabs.sendMessage and runtime.sendMessage. |
| 278 chromeHidden.Port.sendMessageImpl = function(port, request, | 257 function sendMessageImpl(port, request, responseCallback) { |
| 279 responseCallback) { | 258 if (port.name != kNativeMessageChannel) |
| 280 if (port.name != chromeHidden.kNativeMessageChannel) | |
| 281 port.postMessage(request); | 259 port.postMessage(request); |
| 282 | 260 |
| 283 if (port.name == chromeHidden.kMessageChannel && !responseCallback) { | 261 if (port.name == kMessageChannel && !responseCallback) { |
| 284 // TODO(mpcomplete): Do this for the old sendRequest API too, after | 262 // TODO(mpcomplete): Do this for the old sendRequest API too, after |
| 285 // verifying it doesn't break anything. | 263 // verifying it doesn't break anything. |
| 286 // Go ahead and disconnect immediately if the sender is not expecting | 264 // Go ahead and disconnect immediately if the sender is not expecting |
| 287 // a response. | 265 // a response. |
| 288 port.disconnect(); | 266 port.disconnect(); |
| 289 return; | 267 return; |
| 290 } | 268 } |
| 291 | 269 |
| 292 // Ensure the callback exists for the older sendRequest API. | 270 // Ensure the callback exists for the older sendRequest API. |
| 293 if (!responseCallback) | 271 if (!responseCallback) |
| 294 responseCallback = function() {}; | 272 responseCallback = function() {}; |
| 295 | 273 |
| 296 port.onDisconnect.addListener(function() { | 274 port.onDisconnect.addListener(function() { |
| 297 // For onDisconnects, we only notify the callback if there was an error | 275 // For onDisconnects, we only notify the callback if there was an error. |
| 298 try { | 276 try { |
| 299 if (chrome.runtime.lastError) | 277 if (chrome.runtime.lastError) |
| 300 responseCallback(); | 278 responseCallback(); |
| 301 } finally { | 279 } finally { |
| 302 port = null; | 280 port = null; |
| 303 } | 281 } |
| 304 }); | 282 }); |
| 305 port.onMessage.addListener(function(response) { | 283 port.onMessage.addListener(function(response) { |
| 306 try { | 284 try { |
| 307 responseCallback(response); | 285 responseCallback(response); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 331 // targetId (first argument, extensionId in the manfiest) is optional. | 309 // targetId (first argument, extensionId in the manfiest) is optional. |
| 332 var targetId = null; | 310 var targetId = null; |
| 333 if (lastArg >= 0) | 311 if (lastArg >= 0) |
| 334 targetId = args[lastArg--]; | 312 targetId = args[lastArg--]; |
| 335 | 313 |
| 336 if (lastArg != -1) | 314 if (lastArg != -1) |
| 337 throw new Error('Invalid arguments to ' + functionName + '.'); | 315 throw new Error('Invalid arguments to ' + functionName + '.'); |
| 338 return [targetId, request, responseCallback]; | 316 return [targetId, request, responseCallback]; |
| 339 } | 317 } |
| 340 | 318 |
| 319 exports.kRequestChannel = kRequestChannel; |
| 320 exports.kMessageChannel = kMessageChannel; |
| 321 exports.kNativeMessageChannel = kNativeMessageChannel; |
| 322 exports.Port = Port; |
| 323 exports.createPort = createPort; |
| 324 exports.sendMessageImpl = sendMessageImpl; |
| 341 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; | 325 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; |
| 326 |
| 327 // For C++ code to call. |
| 328 exports.hasPort = hasPort; |
| 329 exports.dispatchOnConnect = dispatchOnConnect; |
| 330 exports.dispatchOnDisconnect = dispatchOnDisconnect; |
| 331 exports.dispatchOnMessage = dispatchOnMessage; |
| OLD | NEW |