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.TargetAwareObject} 34 * @param {!WebInspector.Target} target 35 */ 36WebInspector.ConsoleModel = function(target) 37{ 38 WebInspector.TargetAwareObject.call(this, 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 * @param {boolean=} isFromBackend 84 */ 85 addMessage: function(msg, isFromBackend) 86 { 87 if (isFromBackend && WebInspector.NetworkManager.hasDevToolsRequestHeader(msg.request)) 88 return; 89 90 msg.index = this.messages.length; 91 this.messages.push(msg); 92 this._incrementErrorWarningCount(msg); 93 94 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg); 95 }, 96 97 show: function() 98 { 99 WebInspector.Revealer.reveal(this); 100 }, 101 102 /** 103 * @param {string} messageText 104 * @param {!WebInspector.ConsoleMessage.MessageLevel=} messageLevel 105 * @param {boolean=} showConsole 106 */ 107 log: function(messageText, messageLevel, showConsole) 108 { 109 var message = new WebInspector.ConsoleMessage( 110 this.target(), 111 WebInspector.ConsoleMessage.MessageSource.Other, 112 messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug, 113 messageText); 114 115 this.addMessage(message); 116 if (showConsole) 117 this.show(); 118 }, 119 120 /** 121 * @param {string} error 122 */ 123 showErrorMessage: function(error) 124 { 125 this.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true); 126 }, 127 128 /** 129 * @param {!WebInspector.ConsoleMessage} msg 130 */ 131 _incrementErrorWarningCount: function(msg) 132 { 133 switch (msg.level) { 134 case WebInspector.ConsoleMessage.MessageLevel.Warning: 135 this.warnings++; 136 break; 137 case WebInspector.ConsoleMessage.MessageLevel.Error: 138 this.errors++; 139 break; 140 } 141 }, 142 143 requestClearMessages: function() 144 { 145 this._consoleAgent.clearMessages(); 146 this.clearMessages(); 147 }, 148 149 clearMessages: function() 150 { 151 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.ConsoleCleared); 152 153 this.messages = []; 154 this.errors = 0; 155 this.warnings = 0; 156 }, 157 158 __proto__: WebInspector.TargetAwareObject.prototype 159} 160 161/** 162 * @param {!WebInspector.ExecutionContext} executionContext 163 * @param {string} text 164 * @param {boolean=} useCommandLineAPI 165 */ 166WebInspector.ConsoleModel.evaluateCommandInConsole = function(executionContext, text, useCommandLineAPI) 167{ 168 useCommandLineAPI = !!useCommandLineAPI; 169 var target = executionContext.target(); 170 171 var commandMessage = new WebInspector.ConsoleMessage(target, WebInspector.ConsoleMessage.MessageSource.JS, null, text, WebInspector.ConsoleMessage.MessageType.Command); 172 target.consoleModel.addMessage(commandMessage); 173 174 /** 175 * @param {?WebInspector.RemoteObject} result 176 * @param {boolean} wasThrown 177 * @param {?RuntimeAgent.RemoteObject=} valueResult 178 * @this {WebInspector.ConsoleModel} 179 */ 180 function printResult(result, wasThrown, valueResult) 181 { 182 if (!result) 183 return; 184 185 this.show(); 186 this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.CommandEvaluated, {result: result, wasThrown: wasThrown, text: text, commandMessage: commandMessage}); 187 } 188 189 executionContext.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(target.consoleModel)); 190 191 WebInspector.userMetrics.ConsoleEvaluated.record(); 192} 193 194 195/** 196 * @constructor 197 * @param {?WebInspector.Target} target 198 * @param {string} source 199 * @param {?string} level 200 * @param {string} messageText 201 * @param {string=} type 202 * @param {?string=} url 203 * @param {number=} line 204 * @param {number=} column 205 * @param {!NetworkAgent.RequestId=} requestId 206 * @param {!Array.<!RuntimeAgent.RemoteObject>=} parameters 207 * @param {!Array.<!ConsoleAgent.CallFrame>=} stackTrace 208 * @param {number=} timestamp 209 * @param {boolean=} isOutdated 210 * @param {!RuntimeAgent.ExecutionContextId=} executionContextId 211 */ 212WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, isOutdated, executionContextId) 213{ 214 this._target = target; 215 this.source = source; 216 this.level = level; 217 this.messageText = messageText; 218 this.type = type || WebInspector.ConsoleMessage.MessageType.Log; 219 this.url = url || null; 220 this.line = line || 0; 221 this.column = column || 0; 222 this.parameters = parameters; 223 this.stackTrace = stackTrace; 224 this.timestamp = timestamp || Date.now(); 225 this.isOutdated = isOutdated; 226 this.executionContextId = executionContextId || 0; 227 228 this.request = requestId ? target.networkLog.requestForId(requestId) : null; 229 230 if (this.request) { 231 this.stackTrace = this.request.initiator.stackTrace; 232 if (this.request.initiator && this.request.initiator.url) { 233 this.url = this.request.initiator.url; 234 this.line = this.request.initiator.lineNumber; 235 } 236 } 237} 238 239WebInspector.ConsoleMessage.prototype = { 240 /** 241 * @return {?WebInspector.Target} 242 */ 243 target: function() 244 { 245 return this._target; 246 }, 247 248 /** 249 * @param {!WebInspector.ConsoleMessage} originatingMessage 250 */ 251 setOriginatingMessage: function(originatingMessage) 252 { 253 this._originatingConsoleMessage = originatingMessage; 254 }, 255 256 /** 257 * @return {?WebInspector.ConsoleMessage} 258 */ 259 originatingMessage: function() 260 { 261 return this._originatingConsoleMessage; 262 }, 263 264 /** 265 * @return {boolean} 266 */ 267 isGroupMessage: function() 268 { 269 return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || 270 this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed || 271 this.type === WebInspector.ConsoleMessage.MessageType.EndGroup; 272 }, 273 274 /** 275 * @return {boolean} 276 */ 277 isGroupStartMessage: function() 278 { 279 return this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || 280 this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed; 281 }, 282 283 /** 284 * @return {boolean} 285 */ 286 isErrorOrWarning: function() 287 { 288 return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error); 289 }, 290 291 /** 292 * @return {!WebInspector.ConsoleMessage} 293 */ 294 clone: function() 295 { 296 return new WebInspector.ConsoleMessage( 297 this.target(), 298 this.source, 299 this.level, 300 this.messageText, 301 this.type, 302 this.url, 303 this.line, 304 this.column, 305 this.request ? this.request.requestId : undefined, 306 this.parameters, 307 this.stackTrace, 308 this.timestamp, 309 this.isOutdated, 310 this.executionContextId); 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.stackTrace) { 323 if (!msg.stackTrace || this.stackTrace.length !== msg.stackTrace.length) 324 return false; 325 326 for (var i = 0; i < msg.stackTrace.length; ++i) { 327 if (this.stackTrace[i].url !== msg.stackTrace[i].url || 328 this.stackTrace[i].functionName !== msg.stackTrace[i].functionName || 329 this.stackTrace[i].lineNumber !== msg.stackTrace[i].lineNumber || 330 this.stackTrace[i].columnNumber !== msg.stackTrace[i].columnNumber) 331 return false; 332 } 333 } 334 335 if (this.parameters) { 336 if (!msg.parameters || this.parameters.length !== msg.parameters.length) 337 return false; 338 339 for (var i = 0; i < msg.parameters.length; ++i) { 340 // Never treat objects as equal - their properties might change over time. 341 if (this.parameters[i].type !== msg.parameters[i].type || msg.parameters[i].type === "object" || this.parameters[i].value !== msg.parameters[i].value) 342 return false; 343 } 344 } 345 346 return (this.target() === msg.target()) 347 && (this.source === msg.source) 348 && (this.type === msg.type) 349 && (this.level === msg.level) 350 && (this.line === msg.line) 351 && (this.url === msg.url) 352 && (this.messageText === msg.messageText) 353 && (this.request === msg.request) 354 && (this.executionContextId === msg.executionContextId); 355 } 356} 357 358// Note: Keep these constants in sync with the ones in Console.h 359/** 360 * @enum {string} 361 */ 362WebInspector.ConsoleMessage.MessageSource = { 363 XML: "xml", 364 JS: "javascript", 365 Network: "network", 366 ConsoleAPI: "console-api", 367 Storage: "storage", 368 AppCache: "appcache", 369 Rendering: "rendering", 370 CSS: "css", 371 Security: "security", 372 Other: "other", 373 Deprecation: "deprecation" 374} 375 376/** 377 * @enum {string} 378 */ 379WebInspector.ConsoleMessage.MessageType = { 380 Log: "log", 381 Dir: "dir", 382 DirXML: "dirxml", 383 Table: "table", 384 Trace: "trace", 385 Clear: "clear", 386 StartGroup: "startGroup", 387 StartGroupCollapsed: "startGroupCollapsed", 388 EndGroup: "endGroup", 389 Assert: "assert", 390 Result: "result", 391 Profile: "profile", 392 ProfileEnd: "profileEnd", 393 Command: "command" 394} 395 396/** 397 * @enum {string} 398 */ 399WebInspector.ConsoleMessage.MessageLevel = { 400 Log: "log", 401 Info: "info", 402 Warning: "warning", 403 Error: "error", 404 Debug: "debug" 405} 406 407/** 408 * @param {!WebInspector.ConsoleMessage} a 409 * @param {!WebInspector.ConsoleMessage} b 410 * @return {number} 411 */ 412WebInspector.ConsoleMessage.timestampComparator = function (a, b) 413{ 414 return a.timestamp - b.timestamp; 415} 416 417/** 418 * @constructor 419 * @implements {ConsoleAgent.Dispatcher} 420 * @param {!WebInspector.ConsoleModel} console 421 */ 422WebInspector.ConsoleDispatcher = function(console) 423{ 424 this._console = console; 425} 426 427WebInspector.ConsoleDispatcher.prototype = { 428 /** 429 * @param {!ConsoleAgent.ConsoleMessage} payload 430 */ 431 messageAdded: function(payload) 432 { 433 var consoleMessage = new WebInspector.ConsoleMessage( 434 this._console.target(), 435 payload.source, 436 payload.level, 437 payload.text, 438 payload.type, 439 payload.url, 440 payload.line, 441 payload.column, 442 payload.networkRequestId, 443 payload.parameters, 444 payload.stackTrace, 445 payload.timestamp * 1000, // Convert to ms. 446 this._console._enablingConsole, 447 payload.executionContextId); 448 this._console.addMessage(consoleMessage, true); 449 }, 450 451 /** 452 * @param {number} count 453 */ 454 messageRepeatCountUpdated: function(count) 455 { 456 }, 457 458 messagesCleared: function() 459 { 460 if (!WebInspector.settings.preserveConsoleLog.get()) 461 this._console.clearMessages(); 462 } 463} 464 465/** 466 * @type {!WebInspector.ConsoleModel} 467 */ 468WebInspector.console; 469