1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.SDKModel} 34 * @param {!WebInspector.Target} target 35 */ 36WebInspector.ConsoleModel = function(target) 37{ 38 WebInspector.SDKModel.call(this, WebInspector.ConsoleModel, target); 39 40 /** @type {!Array.<!WebInspector.ConsoleMessage>} */ 41 this._messages = []; 42 this.warnings = 0; 43 this.errors = 0; 44 this._consoleAgent = target.consoleAgent(); 45 target.registerConsoleDispatcher(new WebInspector.ConsoleDispatcher(this)); 46 this._enableAgent(); 47} 48 49WebInspector.ConsoleModel.Events = { 50 ConsoleCleared: "ConsoleCleared", 51 MessageAdded: "MessageAdded", 52 CommandEvaluated: "CommandEvaluated", 53} 54 55WebInspector.ConsoleModel.prototype = { 56 _enableAgent: function() 57 { 58 if (WebInspector.settings.monitoringXHREnabled.get()) 59 this._consoleAgent.setMonitoringXHREnabled(true); 60 61 this._enablingConsole = true; 62 63 /** 64 * @this {WebInspector.ConsoleModel} 65 */ 66 function callback() 67 { 68 delete this._enablingConsole; 69 } 70 this._consoleAgent.enable(callback.bind(this)); 71 }, 72 73 /** 74 * @return {boolean} 75 */ 76 enablingConsole: function() 77 { 78 return !!this._enablingConsole; 79 }, 80 81 /** 82 * @param {!WebInspector.ConsoleMessage} msg 83 */ 84 addMessage: function(msg) 85 { 86 if (WebInspector.NetworkManager.hasDevToolsRequestHeader(msg.request)) 87 return; 88 89 msg.index = this._messages.length; 90 this._messages.push(msg); 91 this._incrementErrorWarningCount(msg); 92 93 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg); 94 }, 95 96 /** 97 * @param {!WebInspector.ConsoleMessage} msg 98 */ 99 _incrementErrorWarningCount: function(msg) 100 { 101 switch (msg.level) { 102 case WebInspector.ConsoleMessage.MessageLevel.Warning: 103 this.warnings++; 104 break; 105 case WebInspector.ConsoleMessage.MessageLevel.Error: 106 this.errors++; 107 break; 108 } 109 }, 110 111 /** 112 * @return {!Array.<!WebInspector.ConsoleMessage>} 113 */ 114 messages: function() 115 { 116 return this._messages; 117 }, 118 119 requestClearMessages: function() 120 { 121 this._consoleAgent.clearMessages(); 122 this._messagesCleared(); 123 }, 124 125 _messagesCleared: function() 126 { 127 this._messages = []; 128 this.errors = 0; 129 this.warnings = 0; 130 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared); 131 }, 132 133 __proto__: WebInspector.SDKModel.prototype 134} 135 136/** 137 * @param {!WebInspector.ExecutionContext} executionContext 138 * @param {string} text 139 * @param {boolean=} useCommandLineAPI 140 */ 141WebInspector.ConsoleModel.evaluateCommandInConsole = function(executionContext, text, useCommandLineAPI) 142{ 143 useCommandLineAPI = !!useCommandLineAPI; 144 var target = executionContext.target(); 145 146 var commandMessage = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, null, text, WebInspector.ConsoleMessage.MessageType.Command); 147 commandMessage.setExecutionContextId(executionContext.id); 148 target.consoleModel.addMessage(commandMessage); 149 150 /** 151 * @param {?WebInspector.RemoteObject} result 152 * @param {boolean} wasThrown 153 * @param {?RuntimeAgent.RemoteObject=} valueResult 154 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails 155 * @this {WebInspector.ConsoleModel} 156 */ 157 function printResult(result, wasThrown, valueResult, exceptionDetails) 158 { 159 if (!result) 160 return; 161 162 WebInspector.console.show(); 163 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.CommandEvaluated, {result: result, wasThrown: wasThrown, text: text, commandMessage: commandMessage, exceptionDetails: exceptionDetails}); 164 } 165 166 executionContext.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(target.consoleModel)); 167 168 WebInspector.userMetrics.ConsoleEvaluated.record(); 169} 170 171 172/** 173 * @constructor 174 * @param {?WebInspector.Target} target 175 * @param {string} source 176 * @param {?string} level 177 * @param {string} messageText 178 * @param {string=} type 179 * @param {?string=} url 180 * @param {number=} line 181 * @param {number=} column 182 * @param {!NetworkAgent.RequestId=} requestId 183 * @param {!Array.<!RuntimeAgent.RemoteObject>=} parameters 184 * @param {!Array.<!ConsoleAgent.CallFrame>=} stackTrace 185 * @param {number=} timestamp 186 * @param {boolean=} isOutdated 187 * @param {!RuntimeAgent.ExecutionContextId=} executionContextId 188 * @param {!ConsoleAgent.AsyncStackTrace=} asyncStackTrace 189 * @param {?string=} scriptId 190 */ 191WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, isOutdated, executionContextId, asyncStackTrace, scriptId) 192{ 193 this._target = target; 194 this.source = source; 195 this.level = level; 196 this.messageText = messageText; 197 this.type = type || WebInspector.ConsoleMessage.MessageType.Log; 198 /** @type {string|undefined} */ 199 this.url = url || undefined; 200 /** @type {number} */ 201 this.line = line || 0; 202 /** @type {number} */ 203 this.column = column || 0; 204 this.parameters = parameters; 205 /** @type {!Array.<!ConsoleAgent.CallFrame>|undefined} */ 206 this.stackTrace = stackTrace; 207 this.timestamp = timestamp || Date.now(); 208 this.isOutdated = isOutdated; 209 this.executionContextId = executionContextId || 0; 210 this.asyncStackTrace = asyncStackTrace; 211 this.scriptId = scriptId || null; 212 213 this.request = requestId ? target.networkLog.requestForId(requestId) : null; 214 215 if (this.request) { 216 var initiator = this.request.initiator(); 217 if (initiator) { 218 this.stackTrace = initiator.stackTrace || undefined; 219 this.asyncStackTrace = initiator.asyncStackTrace; 220 if (initiator.url) { 221 this.url = initiator.url; 222 this.line = initiator.lineNumber || 0; 223 } 224 } 225 } 226} 227 228WebInspector.ConsoleMessage.prototype = { 229 /** 230 * @return {?WebInspector.Target} 231 */ 232 target: function() 233 { 234 return this._target; 235 }, 236 237 /** 238 * @param {!WebInspector.ConsoleMessage} originatingMessage 239 */ 240 setOriginatingMessage: function(originatingMessage) 241 { 242 this._originatingConsoleMessage = originatingMessage; 243 this.executionContextId = originatingMessage.executionContextId; 244 }, 245 246 /** 247 * @param {!RuntimeAgent.ExecutionContextId} executionContextId 248 */ 249 setExecutionContextId: function(executionContextId) 250 { 251 this.executionContextId = executionContextId; 252 }, 253 254 /** 255 * @return {?WebInspector.ConsoleMessage} 256 */ 257 originatingMessage: function() 258 { 259 return this._originatingConsoleMessage; 260 }, 261 262 /** 263 * @return {boolean} 264 */ 265 isGroupMessage: function() 266 { 267 return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || 268 this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || 269 this.type === WebInspector.ConsoleMessage.MessageType.EndGroup; 270 }, 271 272 /** 273 * @return {boolean} 274 */ 275 isGroupStartMessage: function() 276 { 277 return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || 278 this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed; 279 }, 280 281 /** 282 * @return {boolean} 283 */ 284 isErrorOrWarning: function() 285 { 286 return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error); 287 }, 288 289 /** 290 * @return {!WebInspector.ConsoleMessage} 291 */ 292 clone: function() 293 { 294 return new WebInspector.ConsoleMessage( 295 this.target(), 296 this.source, 297 this.level, 298 this.messageText, 299 this.type, 300 this.url, 301 this.line, 302 this.column, 303 this.request ? this.request.requestId : undefined, 304 this.parameters, 305 this.stackTrace, 306 this.timestamp, 307 this.isOutdated, 308 this.executionContextId, 309 this.asyncStackTrace, 310 this.scriptId); 311 }, 312 313 /** 314 * @param {?WebInspector.ConsoleMessage} msg 315 * @return {boolean} 316 */ 317 isEqual: function(msg) 318 { 319 if (!msg) 320 return false; 321 322 if (!this._isEqualStackTraces(this.stackTrace, msg.stackTrace)) 323 return false; 324 325 var asyncTrace1 = this.asyncStackTrace; 326 var asyncTrace2 = msg.asyncStackTrace; 327 while (asyncTrace1 || asyncTrace2) { 328 if (!asyncTrace1 || !asyncTrace2) 329 return false; 330 if (asyncTrace1.description !== asyncTrace2.description) 331 return false; 332 if (!this._isEqualStackTraces(asyncTrace1.callFrames, asyncTrace2.callFrames)) 333 return false; 334 asyncTrace1 = asyncTrace1.asyncStackTrace; 335 asyncTrace2 = asyncTrace2.asyncStackTrace; 336 } 337 338 if (this.parameters) { 339 if (!msg.parameters || this.parameters.length !== msg.parameters.length) 340 return false; 341 342 for (var i = 0; i < msg.parameters.length; ++i) { 343 // Never treat objects as equal - their properties might change over time. 344 if (this.parameters[i].type !== msg.parameters[i].type || msg.parameters[i].type === "object" || this.parameters[i].value !== msg.parameters[i].value) 345 return false; 346 } 347 } 348 349 return (this.target() === msg.target()) 350 && (this.source === msg.source) 351 && (this.type === msg.type) 352 && (this.level === msg.level) 353 && (this.line === msg.line) 354 && (this.url === msg.url) 355 && (this.messageText === msg.messageText) 356 && (this.request === msg.request) 357 && (this.executionContextId === msg.executionContextId) 358 && (this.scriptId === msg.scriptId); 359 }, 360 361 /** 362 * @param {!Array.<!ConsoleAgent.CallFrame>|undefined} stackTrace1 363 * @param {!Array.<!ConsoleAgent.CallFrame>|undefined} stackTrace2 364 * @return {boolean} 365 */ 366 _isEqualStackTraces: function(stackTrace1, stackTrace2) 367 { 368 stackTrace1 = stackTrace1 || []; 369 stackTrace2 = stackTrace2 || []; 370 if (stackTrace1.length !== stackTrace2.length) 371 return false; 372 for (var i = 0, n = stackTrace1.length; i < n; ++i) { 373 if (stackTrace1[i].url !== stackTrace2[i].url || 374 stackTrace1[i].functionName !== stackTrace2[i].functionName || 375 stackTrace1[i].lineNumber !== stackTrace2[i].lineNumber || 376 stackTrace1[i].columnNumber !== stackTrace2[i].columnNumber) 377 return false; 378 } 379 return true; 380 } 381} 382 383// Note: Keep these constants in sync with the ones in Console.h 384/** 385 * @enum {string} 386 */ 387WebInspector.ConsoleMessage.MessageSource = { 388 XML: "xml", 389 JS: "javascript", 390 Network: "network", 391 ConsoleAPI: "console-api", 392 Storage: "storage", 393 AppCache: "appcache", 394 Rendering: "rendering", 395 CSS: "css", 396 Security: "security", 397 Other: "other", 398 Deprecation: "deprecation" 399} 400 401/** 402 * @enum {string} 403 */ 404WebInspector.ConsoleMessage.MessageType = { 405 Log: "log", 406 Dir: "dir", 407 DirXML: "dirxml", 408 Table: "table", 409 Trace: "trace", 410 Clear: "clear", 411 StartGroup: "startGroup", 412 StartGroupCollapsed: "startGroupCollapsed", 413 EndGroup: "endGroup", 414 Assert: "assert", 415 Result: "result", 416 Profile: "profile", 417 ProfileEnd: "profileEnd", 418 Command: "command" 419} 420 421/** 422 * @enum {string} 423 */ 424WebInspector.ConsoleMessage.MessageLevel = { 425 Log: "log", 426 Info: "info", 427 Warning: "warning", 428 Error: "error", 429 Debug: "debug" 430}; 431 432WebInspector.ConsoleMessage._messageLevelPriority = { 433 "debug": 0, 434 "log": 1, 435 "info": 2, 436 "warning": 3, 437 "error": 4 438}; 439 440/** 441 * @param {!WebInspector.ConsoleMessage} a 442 * @param {!WebInspector.ConsoleMessage} b 443 * @return {number} 444 */ 445WebInspector.ConsoleMessage.messageLevelComparator = function(a, b) 446{ 447 return WebInspector.ConsoleMessage._messageLevelPriority[a.level] - WebInspector.ConsoleMessage._messageLevelPriority[b.level]; 448} 449 450/** 451 * @param {!WebInspector.ConsoleMessage} a 452 * @param {!WebInspector.ConsoleMessage} b 453 * @return {number} 454 */ 455WebInspector.ConsoleMessage.timestampComparator = function (a, b) 456{ 457 return a.timestamp - b.timestamp; 458} 459 460/** 461 * @constructor 462 * @implements {ConsoleAgent.Dispatcher} 463 * @param {!WebInspector.ConsoleModel} console 464 */ 465WebInspector.ConsoleDispatcher = function(console) 466{ 467 this._console = console; 468} 469 470WebInspector.ConsoleDispatcher.prototype = { 471 /** 472 * @param {!ConsoleAgent.ConsoleMessage} payload 473 */ 474 messageAdded: function(payload) 475 { 476 var consoleMessage = new WebInspector.ConsoleMessage( 477 this._console.target(), 478 payload.source, 479 payload.level, 480 payload.text, 481 payload.type, 482 payload.url, 483 payload.line, 484 payload.column, 485 payload.networkRequestId, 486 payload.parameters, 487 payload.stackTrace, 488 payload.timestamp * 1000, // Convert to ms. 489 this._console._enablingConsole, 490 payload.executionContextId, 491 payload.asyncStackTrace, 492 payload.scriptId); 493 this._console.addMessage(consoleMessage); 494 }, 495 496 /** 497 * @param {number} count 498 */ 499 messageRepeatCountUpdated: function(count) 500 { 501 }, 502 503 messagesCleared: function() 504 { 505 if (!WebInspector.settings.preserveConsoleLog.get()) 506 this._console._messagesCleared(); 507 } 508} 509 510/** 511 * @constructor 512 * @extends {WebInspector.Object} 513 * @implements {WebInspector.TargetManager.Observer} 514 */ 515WebInspector.MultitargetConsoleModel = function() 516{ 517 WebInspector.targetManager.observeTargets(this); 518 WebInspector.targetManager.addModelListener(WebInspector.ConsoleModel, WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this); 519 WebInspector.targetManager.addModelListener(WebInspector.ConsoleModel, WebInspector.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this); 520} 521 522WebInspector.MultitargetConsoleModel.prototype = { 523 /** 524 * @param {!WebInspector.Target} target 525 */ 526 targetAdded: function(target) 527 { 528 if (!this._mainTarget) { 529 this._mainTarget = target; 530 target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); 531 } 532 }, 533 534 /** 535 * @param {!WebInspector.Target} target 536 */ 537 targetRemoved: function(target) 538 { 539 if (this._mainTarget === target) { 540 delete this._mainTarget; 541 target.consoleModel.removeEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); 542 } 543 }, 544 545 /** 546 * @return {!Array.<!WebInspector.ConsoleMessage>} 547 */ 548 messages: function() 549 { 550 var targets = WebInspector.targetManager.targets(); 551 var result = []; 552 for (var i = 0; i < targets.length; ++i) 553 result = result.concat(targets[i].consoleModel.messages()); 554 return result; 555 }, 556 557 _consoleCleared: function() 558 { 559 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared); 560 }, 561 562 /** 563 * @param {!WebInspector.Event} event 564 */ 565 _consoleMessageAdded: function(event) 566 { 567 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, event.data); 568 }, 569 570 /** 571 * @param {!WebInspector.Event} event 572 */ 573 _commandEvaluated: function(event) 574 { 575 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.CommandEvaluated, event.data); 576 }, 577 578 __proto__: WebInspector.Object.prototype 579} 580 581/** 582 * @type {!WebInspector.MultitargetConsoleModel} 583 */ 584WebInspector.multitargetConsoleModel; 585