| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * Module to support logging debug messages. | |
| 8 */ | |
| 9 | |
| 10 'use strict'; | |
| 11 | |
| 12 /** @suppress {duplicate} */ | |
| 13 var remoting = remoting || {}; | |
| 14 | |
| 15 /** | |
| 16 * @constructor | |
| 17 * @param {Element} logElement The HTML div to which to add log messages. | |
| 18 * @param {Element} statsElement The HTML div to which to update stats. | |
| 19 */ | |
| 20 remoting.DebugLog = function(logElement, statsElement) { | |
| 21 this.logElement = logElement; | |
| 22 this.statsElement = statsElement; | |
| 23 this.clientJid = ''; | |
| 24 this.hostJid = ''; | |
| 25 }; | |
| 26 | |
| 27 /** | |
| 28 * Maximum number of lines to record in the debug log. Only the most | |
| 29 * recent <n> lines are displayed. | |
| 30 */ | |
| 31 remoting.DebugLog.prototype.MAX_DEBUG_LOG_SIZE = 1000; | |
| 32 | |
| 33 /** | |
| 34 * JID for the remoting bot which is used to bridge communication between | |
| 35 * the Talk network and the Remoting directory service. | |
| 36 */ | |
| 37 remoting.DebugLog.prototype.REMOTING_DIRECTORY_SERVICE_BOT = | |
| 38 'remoting@bot.talk.google.com'; | |
| 39 | |
| 40 /** | |
| 41 * Add the given message to the debug log. | |
| 42 * | |
| 43 * @param {number} indentLevel The indention level for this message. | |
| 44 * @param {string} message The debug info to add to the log. | |
| 45 */ | |
| 46 remoting.DebugLog.prototype.logIndent = function(indentLevel, message) { | |
| 47 // Remove lines from top if we've hit our max log size. | |
| 48 if (this.logElement.childNodes.length == this.MAX_DEBUG_LOG_SIZE) { | |
| 49 this.logElement.removeChild(this.logElement.firstChild); | |
| 50 } | |
| 51 | |
| 52 // Add the new <p> to the end of the debug log. | |
| 53 var p = document.createElement('p'); | |
| 54 if (indentLevel == 1) { | |
| 55 p.className = 'indent'; | |
| 56 } else if (indentLevel > 1) { | |
| 57 p.className = 'indent2'; | |
| 58 } | |
| 59 p.appendChild(document.createTextNode(message)); | |
| 60 this.logElement.appendChild(p); | |
| 61 | |
| 62 // Scroll to bottom of div | |
| 63 this.logElement.scrollTop = this.logElement.scrollHeight; | |
| 64 }; | |
| 65 | |
| 66 /** | |
| 67 * Add the given message to the debug log. | |
| 68 * | |
| 69 * @param {string} message The debug info to add to the log. | |
| 70 */ | |
| 71 remoting.DebugLog.prototype.log = function(message) { | |
| 72 this.logIndent(0, message); | |
| 73 } | |
| 74 | |
| 75 /** | |
| 76 * Show or hide the debug log. | |
| 77 */ | |
| 78 remoting.DebugLog.prototype.toggle = function() { | |
| 79 var debugLog = /** @type {Element} */ this.logElement.parentNode; | |
| 80 if (debugLog.hidden) { | |
| 81 debugLog.hidden = false; | |
| 82 } else { | |
| 83 debugLog.hidden = true; | |
| 84 } | |
| 85 }; | |
| 86 | |
| 87 /** | |
| 88 * Update the statistics panel. | |
| 89 * @param {Object.<string, number>} stats The connection statistics. | |
| 90 */ | |
| 91 remoting.DebugLog.prototype.updateStatistics = function(stats) { | |
| 92 var units = ''; | |
| 93 var videoBandwidth = stats[remoting.ClientSession.STATS_KEY_VIDEO_BANDWIDTH]; | |
| 94 if (videoBandwidth < 1024) { | |
| 95 units = 'Bps'; | |
| 96 } else if (videoBandwidth < 1048576) { | |
| 97 units = 'KiBps'; | |
| 98 videoBandwidth = videoBandwidth / 1024; | |
| 99 } else if (videoBandwidth < 1073741824) { | |
| 100 units = 'MiBps'; | |
| 101 videoBandwidth = videoBandwidth / 1048576; | |
| 102 } else { | |
| 103 units = 'GiBps'; | |
| 104 videoBandwidth = videoBandwidth / 1073741824; | |
| 105 } | |
| 106 | |
| 107 var statistics = document.getElementById('statistics'); | |
| 108 this.statsElement.innerText = | |
| 109 'Bandwidth: ' + videoBandwidth.toFixed(2) + units + | |
| 110 ', Frame Rate: ' + | |
| 111 (stats[remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE] ? | |
| 112 stats[remoting.ClientSession.STATS_KEY_VIDEO_FRAME_RATE].toFixed(2) | |
| 113 + ' fps' : 'n/a') + | |
| 114 ', Capture: ' + | |
| 115 stats[remoting.ClientSession.STATS_KEY_CAPTURE_LATENCY].toFixed(2) + | |
| 116 'ms' + | |
| 117 ', Encode: ' + | |
| 118 stats[remoting.ClientSession.STATS_KEY_ENCODE_LATENCY].toFixed(2) + | |
| 119 'ms' + | |
| 120 ', Decode: ' + | |
| 121 stats[remoting.ClientSession.STATS_KEY_DECODE_LATENCY].toFixed(2) + | |
| 122 'ms' + | |
| 123 ', Render: ' + | |
| 124 stats[remoting.ClientSession.STATS_KEY_RENDER_LATENCY].toFixed(2) + | |
| 125 'ms' + | |
| 126 ', Latency: ' + | |
| 127 stats[remoting.ClientSession.STATS_KEY_ROUNDTRIP_LATENCY].toFixed(2) + | |
| 128 'ms'; | |
| 129 }; | |
| 130 | |
| 131 /** | |
| 132 * Check for the debug toggle hot-key. | |
| 133 * | |
| 134 * @param {Event} event The keyboard event. | |
| 135 * @return {void} Nothing. | |
| 136 */ | |
| 137 remoting.DebugLog.onKeydown = function(event) { | |
| 138 var element = /** @type {Element} */ (event.target); | |
| 139 if (element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') { | |
| 140 return; | |
| 141 } | |
| 142 if (String.fromCharCode(event.which) == 'D') { | |
| 143 remoting.debug.toggle(); | |
| 144 } | |
| 145 }; | |
| 146 | |
| 147 /** | |
| 148 * Verify that the only attributes on the given |node| are those specified | |
| 149 * in the |attrs| string. | |
| 150 * | |
| 151 * @param {Node} node The node to verify. | |
| 152 * @param {string} validAttrs Comma-separated list of valid attributes. | |
| 153 * | |
| 154 * @return {boolean} True if the node contains only valid attributes. | |
| 155 */ | |
| 156 remoting.DebugLog.prototype.verifyAttributes = function(node, validAttrs) { | |
| 157 var attrs = ',' + validAttrs + ','; | |
| 158 var len = node.attributes.length; | |
| 159 for (var i = 0; i < len; i++) { | |
| 160 /** @type {Node} */ | |
| 161 var attrNode = node.attributes[i]; | |
| 162 var attr = attrNode.nodeName; | |
| 163 if (attrs.indexOf(',' + attr + ',') == -1) { | |
| 164 return false; | |
| 165 } | |
| 166 } | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Record the client and host JIDs so that we can check them against the | |
| 172 * params in the IQ packets. | |
| 173 * | |
| 174 * @param {string} clientJid The client JID string. | |
| 175 * @param {string} hostJid The host JID string. | |
| 176 */ | |
| 177 remoting.DebugLog.prototype.setJids = function(clientJid, hostJid) { | |
| 178 this.clientJid = clientJid; | |
| 179 this.hostJid = hostJid; | |
| 180 } | |
| 181 | |
| 182 /** | |
| 183 * Calculate the 'pretty' version of data from the |server| node and return | |
| 184 * it as a string. | |
| 185 * | |
| 186 * @param {Node} server Xml node with server info. | |
| 187 * | |
| 188 * @return {Array} Array of boolean result and pretty-version of |server| node. | |
| 189 */ | |
| 190 remoting.DebugLog.prototype.calcServerString = function(server) { | |
| 191 if (!this.verifyAttributes(server, 'host,udp,tcp,tcpssl')) { | |
| 192 return [false, '']; | |
| 193 } | |
| 194 var host = server.getAttribute('host'); | |
| 195 var udp = server.getAttribute('udp'); | |
| 196 var tcp = server.getAttribute('tcp'); | |
| 197 var tcpssl = server.getAttribute('tcpssl'); | |
| 198 | |
| 199 var str = "'" + host + "'"; | |
| 200 if (udp) | |
| 201 str = str + ' udp:' + udp; | |
| 202 if (tcp) | |
| 203 str = str + ' tcp:' + tcp; | |
| 204 if (tcpssl) | |
| 205 str = str + ' tcpssl:' + tcpssl; | |
| 206 | |
| 207 str = str + '; '; | |
| 208 return [true, str]; | |
| 209 }; | |
| 210 | |
| 211 /** | |
| 212 * Calc the 'pretty' version of channel data and return it as a string. | |
| 213 * | |
| 214 * @param {Node} channel Xml node with channel info. | |
| 215 * | |
| 216 * @return {Array} Array of result and pretty-version of |channel| node. | |
| 217 */ | |
| 218 remoting.DebugLog.prototype.calcChannelString = function(channel) { | |
| 219 var name = channel.nodeName; | |
| 220 if (!this.verifyAttributes(channel, 'transport,version,codec')) { | |
| 221 return [false, '']; | |
| 222 } | |
| 223 var transport = channel.getAttribute('transport'); | |
| 224 var version = channel.getAttribute('version'); | |
| 225 var desc = name + ' ' + transport + ' v' + version; | |
| 226 if (name == 'video') { | |
| 227 desc = desc + ' codec=' + channel.getAttribute('codec'); | |
| 228 } | |
| 229 return [true, desc + '; ']; | |
| 230 } | |
| 231 | |
| 232 /** | |
| 233 * Pretty print the jingleinfo from the given Xml node. | |
| 234 * | |
| 235 * @param {Node} query Xml query node with jingleinfo in the child nodes. | |
| 236 * | |
| 237 * @return {boolean} True if we were able to pretty-print the information. | |
| 238 */ | |
| 239 remoting.DebugLog.prototype.prettyJingleinfo = function(query) { | |
| 240 var nodes = query.childNodes; | |
| 241 var stun_servers = ''; | |
| 242 for (var i = 0; i < nodes.length; i++) { | |
| 243 /** @type {Node} */ | |
| 244 var node = nodes[i]; | |
| 245 var name = node.nodeName; | |
| 246 if (name == 'stun') { | |
| 247 var sserver = ''; | |
| 248 var stun_nodes = node.childNodes; | |
| 249 for(var s = 0; s < stun_nodes.length; s++) { | |
| 250 /** @type {Node} */ | |
| 251 var stun_node = stun_nodes[s]; | |
| 252 var sname = stun_node.nodeName; | |
| 253 if (sname == 'server') { | |
| 254 var stun_sinfo = this.calcServerString(stun_node); | |
| 255 /** @type {boolean} */ | |
| 256 var stun_success = stun_sinfo[0]; | |
| 257 /** @type {string} */ | |
| 258 var stun_sstring = stun_sinfo[1]; | |
| 259 if (!stun_success) { | |
| 260 return false; | |
| 261 } | |
| 262 sserver = sserver + stun_sstring; | |
| 263 } | |
| 264 } | |
| 265 this.logIndent(1, 'stun ' + sserver); | |
| 266 } else if (name == 'relay') { | |
| 267 var token = 'token: '; | |
| 268 var rserver = ''; | |
| 269 var relay_nodes = node.childNodes; | |
| 270 for(var r = 0; r < relay_nodes.length; r++) { | |
| 271 /** @type {Node} */ | |
| 272 var relay_node = relay_nodes[r]; | |
| 273 var rname = relay_node.nodeName; | |
| 274 if (rname == 'token') { | |
| 275 token = token + relay_node.textContent; | |
| 276 } | |
| 277 if (rname == 'server') { | |
| 278 var relay_sinfo = this.calcServerString(relay_node); | |
| 279 /** @type {boolean} */ | |
| 280 var relay_success = relay_sinfo[0]; | |
| 281 /** @type {string} */ | |
| 282 var relay_sstring = relay_sinfo[1]; | |
| 283 if (!relay_success) { | |
| 284 return false; | |
| 285 } | |
| 286 rserver = rserver + relay_sstring; | |
| 287 } | |
| 288 } | |
| 289 this.logIndent(1, 'relay ' + rserver + token); | |
| 290 } else { | |
| 291 return false; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 return true; | |
| 296 }; | |
| 297 | |
| 298 /** | |
| 299 * Pretty print the session-initiate or session-accept info from the given | |
| 300 * Xml node. | |
| 301 * | |
| 302 * @param {Node} jingle Xml node with jingle session-initiate or session-accept | |
| 303 * info contained in child nodes. | |
| 304 * | |
| 305 * @return {boolean} True if we were able to pretty-print the information. | |
| 306 */ | |
| 307 remoting.DebugLog.prototype.prettySessionInitiateAccept = function(jingle) { | |
| 308 if (jingle.childNodes.length != 1) { | |
| 309 return false; | |
| 310 } | |
| 311 var content = jingle.firstChild; | |
| 312 if (content.nodeName != 'content') { | |
| 313 return false; | |
| 314 } | |
| 315 var content_children = content.childNodes; | |
| 316 for (var c = 0; c < content_children.length; c++) { | |
| 317 /** @type {Node} */ | |
| 318 var content_child = content_children[c]; | |
| 319 var cname = content_child.nodeName; | |
| 320 if (cname == 'description') { | |
| 321 var channels = ''; | |
| 322 var resolution = ''; | |
| 323 var auth = ''; | |
| 324 var desc_children = content_child.childNodes; | |
| 325 for (var d = 0; d < desc_children.length; d++) { | |
| 326 /** @type {Node} */ | |
| 327 var desc = desc_children[d]; | |
| 328 var dname = desc.nodeName; | |
| 329 if (dname == 'control' || dname == 'event' || dname == 'video') { | |
| 330 var cinfo = this.calcChannelString(desc); | |
| 331 /** @type {boolean} */ | |
| 332 var success = cinfo[0]; | |
| 333 /** @type {string} */ | |
| 334 var cstring = cinfo[1]; | |
| 335 if (!success) { | |
| 336 return false; | |
| 337 } | |
| 338 channels = channels + cstring; | |
| 339 } else if (dname == 'initial-resolution') { | |
| 340 resolution = desc.getAttribute('width') + 'x' + | |
| 341 desc.getAttribute('height'); | |
| 342 } else if (dname == 'authentication') { | |
| 343 var auth_children = desc.childNodes; | |
| 344 for (var a = 0; a < auth_children.length; a++) { | |
| 345 /** @type {Node} */ | |
| 346 var auth_info = auth_children[a]; | |
| 347 if (auth_info.nodeName == 'auth-token') { | |
| 348 auth = auth + ' (auth-token) ' + auth_info.textContent; | |
| 349 } else if (auth_info.nodeName == 'certificate') { | |
| 350 auth = auth + ' (certificate) ' + auth_info.textContent; | |
| 351 } else if (auth_info.nodeName == 'master-key') { | |
| 352 auth = auth + ' (master-key) ' + auth_info.textContent; | |
| 353 } else { | |
| 354 return false; | |
| 355 } | |
| 356 } | |
| 357 } else { | |
| 358 return false; | |
| 359 } | |
| 360 } | |
| 361 this.logIndent(1, 'channels: ' + channels); | |
| 362 this.logIndent(1, 'auth:' + auth); | |
| 363 this.logIndent(1, 'initial resolution: ' + resolution); | |
| 364 } else if (cname == 'transport') { | |
| 365 // The 'transport' node is currently empty. | |
| 366 var transport_children = content_child.childNodes; | |
| 367 if (transport_children.length != 0) { | |
| 368 return false; | |
| 369 } | |
| 370 } else { | |
| 371 return false; | |
| 372 } | |
| 373 } | |
| 374 return true; | |
| 375 }; | |
| 376 | |
| 377 /** | |
| 378 * Pretty print the session-terminate info from the given Xml node. | |
| 379 * | |
| 380 * @param {Node} jingle Xml node with jingle session-terminate info contained in | |
| 381 * child nodes. | |
| 382 * | |
| 383 * @return {boolean} True if we were able to pretty-print the information. | |
| 384 */ | |
| 385 remoting.DebugLog.prototype.prettySessionTerminate = function(jingle) { | |
| 386 if (jingle.childNodes.length != 1) { | |
| 387 return false; | |
| 388 } | |
| 389 var reason = jingle.firstChild; | |
| 390 if (reason.nodeName != 'reason' || reason.childNodes.length != 1) { | |
| 391 return false; | |
| 392 } | |
| 393 var info = reason.firstChild; | |
| 394 if (info.nodeName == 'success') { | |
| 395 this.logIndent(1, 'reason=success'); | |
| 396 } else { | |
| 397 return false; | |
| 398 } | |
| 399 return true; | |
| 400 }; | |
| 401 | |
| 402 /** | |
| 403 * Pretty print the transport-info info from the given Xml node. | |
| 404 * | |
| 405 * @param {Node} jingle Xml node with jingle transport info contained in child | |
| 406 * nodes. | |
| 407 * | |
| 408 * @return {boolean} True if we were able to pretty-print the information. | |
| 409 */ | |
| 410 remoting.DebugLog.prototype.prettyTransportInfo = function(jingle) { | |
| 411 if (jingle.childNodes.length != 1) { | |
| 412 return false; | |
| 413 } | |
| 414 var content = jingle.firstChild; | |
| 415 if (content.nodeName != 'content') { | |
| 416 return false; | |
| 417 } | |
| 418 var transport = content.firstChild; | |
| 419 if (transport.nodeName != 'transport') { | |
| 420 return false; | |
| 421 } | |
| 422 var transport_children = transport.childNodes; | |
| 423 for (var t = 0; t < transport_children.length; t++) { | |
| 424 /** @type {Node} */ | |
| 425 var candidate = transport_children[t]; | |
| 426 if (candidate.nodeName != 'candidate') { | |
| 427 return false; | |
| 428 } | |
| 429 if (!this.verifyAttributes(candidate, 'name,address,port,preference,' + | |
| 430 'username,protocol,generation,password,type,' + | |
| 431 'network')) { | |
| 432 return false; | |
| 433 } | |
| 434 var name = candidate.getAttribute('name'); | |
| 435 var address = candidate.getAttribute('address'); | |
| 436 var port = candidate.getAttribute('port'); | |
| 437 var pref = candidate.getAttribute('preference'); | |
| 438 var username = candidate.getAttribute('username'); | |
| 439 var protocol = candidate.getAttribute('protocol'); | |
| 440 var generation = candidate.getAttribute('generation'); | |
| 441 var password = candidate.getAttribute('password'); | |
| 442 var type = candidate.getAttribute('type'); | |
| 443 var network = candidate.getAttribute('network'); | |
| 444 | |
| 445 var info = name + ': ' + address + ':' + port + ' ' + protocol + | |
| 446 ' name:' + username + ' pwd:' + password + | |
| 447 ' pref:' + pref + | |
| 448 ' ' + type; | |
| 449 if (network) { | |
| 450 info = info + " network:'" + network + "'"; | |
| 451 } | |
| 452 this.logIndent(1, info) | |
| 453 } | |
| 454 return true; | |
| 455 }; | |
| 456 | |
| 457 /** | |
| 458 * Pretty print the jingle action contained in the given Xml node. | |
| 459 * | |
| 460 * @param {Node} jingle Xml node with jingle action contained in child nodes. | |
| 461 * @param {string} action String containing the jingle action. | |
| 462 * | |
| 463 * @return {boolean} True if we were able to pretty-print the information. | |
| 464 */ | |
| 465 remoting.DebugLog.prototype.prettyJingleAction = function(jingle, action) { | |
| 466 if (action == 'session-initiate' || action == 'session-accept') { | |
| 467 return this.prettySessionInitiateAccept(jingle); | |
| 468 } | |
| 469 if (action == 'session-terminate') { | |
| 470 return this.prettySessionTerminate(jingle); | |
| 471 } | |
| 472 if (action == 'transport-info') { | |
| 473 return this.prettyTransportInfo(jingle); | |
| 474 } | |
| 475 return false; | |
| 476 }; | |
| 477 | |
| 478 /** | |
| 479 * Pretty print the jingle error information contained in the given Xml node. | |
| 480 * | |
| 481 * @param {Node} error Xml node containing error information in child nodes. | |
| 482 * | |
| 483 * @return {boolean} True if we were able to pretty-print the information. | |
| 484 */ | |
| 485 remoting.DebugLog.prototype.prettyError = function(error) { | |
| 486 if (!this.verifyAttributes(error, 'xmlns:err,code,type,err:hostname,' + | |
| 487 'err:bnsname,err:stacktrace')) { | |
| 488 return false; | |
| 489 } | |
| 490 var code = error.getAttribute('code'); | |
| 491 var type = error.getAttribute('type'); | |
| 492 var hostname = error.getAttribute('err:hostname'); | |
| 493 var bnsname = error.getAttribute('err:bnsname'); | |
| 494 var stacktrace = error.getAttribute('err:stacktrace'); | |
| 495 this.logIndent(1, 'error ' + code + ' ' + type + " hostname:'" + | |
| 496 hostname + "' bnsname:'" + bnsname + "'"); | |
| 497 var children = error.childNodes; | |
| 498 for (var i = 0; i < children.length; i++) { | |
| 499 /** @type {Node} */ | |
| 500 var child = children[i]; | |
| 501 this.logIndent(1, child.nodeName); | |
| 502 } | |
| 503 if (stacktrace) { | |
| 504 var stack = stacktrace.split(' | '); | |
| 505 this.logIndent(1, 'stacktrace:'); | |
| 506 // We use 'length-1' because the stack trace ends with " | " which results | |
| 507 // in an empty string at the end after the split. | |
| 508 for (var s = 0; s < stack.length - 1; s++) { | |
| 509 this.logIndent(2, stack[s]); | |
| 510 } | |
| 511 } | |
| 512 return true; | |
| 513 }; | |
| 514 | |
| 515 /** | |
| 516 * Print out the heading line for an iq node. | |
| 517 * | |
| 518 * @param {string} action String describing action (send/receive). | |
| 519 * @param {string} id Packet id. | |
| 520 * @param {string} desc Description of iq action for this node. | |
| 521 * @param {string|null} sid Session id. | |
| 522 */ | |
| 523 remoting.DebugLog.prototype.prettyIqHeading = function(action, id, desc, sid) { | |
| 524 var message = 'iq ' + action + ' id=' + id; | |
| 525 if (desc) { | |
| 526 message = message + ' ' + desc; | |
| 527 } | |
| 528 if (sid) { | |
| 529 message = message + ' sid=' + sid; | |
| 530 } | |
| 531 this.log(message); | |
| 532 } | |
| 533 | |
| 534 /** | |
| 535 * Print out an iq 'result'-type node. | |
| 536 * | |
| 537 * @param {string} action String describing action (send/receive). | |
| 538 * @param {NodeList} iq_list Node list containing the 'result' xml. | |
| 539 * | |
| 540 * @return {boolean} True if the data was logged successfully. | |
| 541 */ | |
| 542 remoting.DebugLog.prototype.prettyIqResult = function(action, iq_list) { | |
| 543 /** @type {Node} */ | |
| 544 var iq = iq_list[0]; | |
| 545 var id = iq.getAttribute('id'); | |
| 546 var iq_children = iq.childNodes; | |
| 547 | |
| 548 if (iq_children.length == 0) { | |
| 549 this.prettyIqHeading(action, id, 'result (empty)', null); | |
| 550 return true; | |
| 551 } else if (iq_children.length == 1) { | |
| 552 /** @type {Node} */ | |
| 553 var child = iq_children[0]; | |
| 554 if (child.nodeName == 'query') { | |
| 555 if (!this.verifyAttributes(child, 'xmlns')) { | |
| 556 return false; | |
| 557 } | |
| 558 var xmlns = child.getAttribute('xmlns'); | |
| 559 if (xmlns == 'google:jingleinfo') { | |
| 560 this.prettyIqHeading(action, id, 'result ' + xmlns, null); | |
| 561 return this.prettyJingleinfo(child); | |
| 562 } | |
| 563 return true; | |
| 564 } else if (child.nodeName == 'rem:log-result') { | |
| 565 if (!this.verifyAttributes(child, 'xmlns:rem')) { | |
| 566 return false; | |
| 567 } | |
| 568 this.prettyIqHeading(action, id, 'result (log-result)', null); | |
| 569 return true; | |
| 570 } | |
| 571 } | |
| 572 return false; | |
| 573 } | |
| 574 | |
| 575 /** | |
| 576 * Print out an iq 'get'-type node. | |
| 577 * | |
| 578 * @param {string} action String describing action (send/receive). | |
| 579 * @param {NodeList} iq_list Node containing the 'get' xml. | |
| 580 * | |
| 581 * @return {boolean} True if the data was logged successfully. | |
| 582 */ | |
| 583 remoting.DebugLog.prototype.prettyIqGet = function(action, iq_list) { | |
| 584 /** @type {Node} */ | |
| 585 var iq = iq_list[0]; | |
| 586 var id = iq.getAttribute('id'); | |
| 587 var iq_children = iq.childNodes; | |
| 588 | |
| 589 if (iq_children.length != 1) { | |
| 590 return false; | |
| 591 } | |
| 592 | |
| 593 /** @type {Node} */ | |
| 594 var query = iq_children[0]; | |
| 595 if (query.nodeName != 'query') { | |
| 596 return false; | |
| 597 } | |
| 598 if (!this.verifyAttributes(query, 'xmlns')) { | |
| 599 return false; | |
| 600 } | |
| 601 var xmlns = query.getAttribute('xmlns'); | |
| 602 this.prettyIqHeading(action, id, 'get ' + xmlns, null); | |
| 603 return true; | |
| 604 } | |
| 605 | |
| 606 /** | |
| 607 * Print out an iq 'set'-type node. | |
| 608 * | |
| 609 * @param {string} action String describing action (send/receive). | |
| 610 * @param {NodeList} iq_list Node containing the 'set' xml. | |
| 611 * | |
| 612 * @return {boolean} True if the data was logged successfully. | |
| 613 */ | |
| 614 remoting.DebugLog.prototype.prettyIqSet = function(action, iq_list) { | |
| 615 /** @type {Node} */ | |
| 616 var iq = iq_list[0]; | |
| 617 var id = iq.getAttribute('id'); | |
| 618 var iq_children = iq.childNodes; | |
| 619 | |
| 620 var children = iq_children.length; | |
| 621 if (children >= 1) { | |
| 622 /** @type {Node} */ | |
| 623 var child = iq_children[0]; | |
| 624 if (child.nodeName == 'jingle') { | |
| 625 var jingle = child; | |
| 626 if (!this.verifyAttributes(jingle, 'xmlns,action,sid,initiator')) { | |
| 627 return false; | |
| 628 } | |
| 629 | |
| 630 var jingle_action = jingle.getAttribute('action'); | |
| 631 var sid = jingle.getAttribute('sid'); | |
| 632 | |
| 633 if (children == 1) { | |
| 634 this.prettyIqHeading(action, id, 'set ' + jingle_action, sid); | |
| 635 return this.prettyJingleAction(jingle, jingle_action); | |
| 636 | |
| 637 } else if (children == 2) { | |
| 638 if (jingle_action == 'session-initiate') { | |
| 639 this.prettyIqHeading(action, id, 'set ' + jingle_action, sid); | |
| 640 if (!this.prettySessionInitiateAccept(jingle)) { | |
| 641 return false; | |
| 642 } | |
| 643 | |
| 644 // When there are two child nodes for a 'session-initiate' node, | |
| 645 // the second is a duplicate copy of the 'session' info added by | |
| 646 // libjingle for backward compatability with an older version of | |
| 647 // Jingle (called Gingle). | |
| 648 // Since M16 we no longer use libjingle on the client side and thus | |
| 649 // we no longer generate this duplicate node. | |
| 650 | |
| 651 // Require that second child is 'session' with type='initiate'. | |
| 652 /** @type {Node} */ | |
| 653 var session = iq_children[1]; | |
| 654 if (session.nodeName != 'session') { | |
| 655 return false; | |
| 656 } | |
| 657 var type = session.getAttribute('type'); | |
| 658 if (type != 'initiate') { | |
| 659 return false; | |
| 660 } | |
| 661 // Silently ignore contents of 'session' node. | |
| 662 return true; | |
| 663 } | |
| 664 } | |
| 665 } | |
| 666 } | |
| 667 return false; | |
| 668 }; | |
| 669 | |
| 670 /** | |
| 671 * Print out an iq 'error'-type node. | |
| 672 * | |
| 673 * @param {string} action String describing action (send/receive). | |
| 674 * @param {NodeList} iq_list Node containing the 'error' xml. | |
| 675 * | |
| 676 * @return {boolean} True if the data was logged successfully. | |
| 677 */ | |
| 678 remoting.DebugLog.prototype.prettyIqError = function(action, iq_list) { | |
| 679 /** @type {Node} */ | |
| 680 var iq = iq_list[0]; | |
| 681 var id = iq.getAttribute('id'); | |
| 682 var iq_children = iq.childNodes; | |
| 683 | |
| 684 var children = iq_children.length; | |
| 685 if (children != 2) { | |
| 686 return false; | |
| 687 } | |
| 688 | |
| 689 /** @type {Node} */ | |
| 690 var jingle = iq_children[0]; | |
| 691 if (jingle.nodeName != 'jingle') { | |
| 692 return false; | |
| 693 } | |
| 694 if (!this.verifyAttributes(jingle, 'xmlns,action,sid')) { | |
| 695 return false; | |
| 696 } | |
| 697 var jingle_action = jingle.getAttribute('action'); | |
| 698 var sid = jingle.getAttribute('sid'); | |
| 699 this.prettyIqHeading(action, id, 'error from ' + jingle_action, sid); | |
| 700 if (!this.prettyJingleAction(jingle, jingle_action)) { | |
| 701 return false; | |
| 702 } | |
| 703 | |
| 704 /** @type {Node} */ | |
| 705 var error = iq_children[1]; | |
| 706 if (error.nodeName != 'cli:error') { | |
| 707 return false; | |
| 708 } | |
| 709 return this.prettyError(error); | |
| 710 } | |
| 711 | |
| 712 /** | |
| 713 * Try to log a pretty-print the given IQ stanza (XML). | |
| 714 * Return true if the stanza was successfully printed. | |
| 715 * | |
| 716 * @param {boolean} send True if we're sending this stanza; false for receiving. | |
| 717 * @param {string} message The XML stanza to add to the log. | |
| 718 * | |
| 719 * @return {boolean} True if the stanza was logged. | |
| 720 */ | |
| 721 remoting.DebugLog.prototype.prettyIq = function(send, message) { | |
| 722 var parser = new DOMParser(); | |
| 723 var xml = parser.parseFromString(message, 'text/xml'); | |
| 724 | |
| 725 var iq_list = xml.getElementsByTagName('iq'); | |
| 726 | |
| 727 if (iq_list && iq_list.length > 0) { | |
| 728 /** @type {Node} */ | |
| 729 var iq = iq_list[0]; | |
| 730 if (!this.verifyAttributes(iq, 'xmlns,xmlns:cli,id,to,from,type')) | |
| 731 return false; | |
| 732 | |
| 733 // Verify that the to/from fields match the expected sender/receiver. | |
| 734 var to = iq.getAttribute('to'); | |
| 735 var from = iq.getAttribute('from'); | |
| 736 var action = ''; | |
| 737 var bot = remoting.DebugLog.prototype.REMOTING_DIRECTORY_SERVICE_BOT; | |
| 738 if (send) { | |
| 739 if (to && to != this.hostJid && to != bot) { | |
| 740 this.log('bad to: ' + to); | |
| 741 return false; | |
| 742 } | |
| 743 if (from && from != this.clientJid) { | |
| 744 this.log('bad from: ' + from); | |
| 745 return false; | |
| 746 } | |
| 747 | |
| 748 action = "send"; | |
| 749 if (to == bot) { | |
| 750 action = action + " (to bot)"; | |
| 751 } | |
| 752 } else { | |
| 753 if (to && to != this.clientJid) { | |
| 754 this.log('bad to: ' + to); | |
| 755 return false; | |
| 756 } | |
| 757 if (from && from != this.hostJid && from != bot) { | |
| 758 this.log('bad from: ' + from); | |
| 759 return false; | |
| 760 } | |
| 761 | |
| 762 action = "receive"; | |
| 763 if (from == bot) { | |
| 764 action = action + " (from bot)"; | |
| 765 } | |
| 766 } | |
| 767 | |
| 768 var type = iq.getAttribute('type'); | |
| 769 if (type == 'result') { | |
| 770 return this.prettyIqResult(action, iq_list); | |
| 771 } else if (type == 'get') { | |
| 772 return this.prettyIqGet(action, iq_list); | |
| 773 } else if (type == 'set') { | |
| 774 return this.prettyIqSet(action, iq_list); | |
| 775 } else if (type == 'error') { | |
| 776 return this.prettyIqError(action, iq_list); | |
| 777 } | |
| 778 } | |
| 779 | |
| 780 return false; | |
| 781 }; | |
| 782 | |
| 783 /** | |
| 784 * Add the given IQ stanza to the debug log. | |
| 785 * When possible, the stanza string will be simplified to make it more | |
| 786 * readable. | |
| 787 * | |
| 788 * @param {boolean} send True if we're sending this stanza; false for receiving. | |
| 789 * @param {string} message The XML stanza to add to the log. | |
| 790 */ | |
| 791 remoting.DebugLog.prototype.logIq = function(send, message) { | |
| 792 if (!this.prettyIq(send, message)) { | |
| 793 // Fall back to showing the raw stanza. | |
| 794 var prefix = (send ? 'Sending Iq: ' : 'Receiving Iq: '); | |
| 795 this.log(prefix + message); | |
| 796 } | |
| 797 }; | |
| 798 | |
| 799 /** @type {remoting.DebugLog} */ | |
| 800 remoting.debug = null; | |
| OLD | NEW |