1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 4 * Copyright (C) 2009 Joseph Pecoraro 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.ConsoleMessage} 34 * 35 * @param {string} source 36 * @param {string} level 37 * @param {string} message 38 * @param {WebInspector.Linkifier} linkifier 39 * @param {string=} type 40 * @param {string=} url 41 * @param {number=} line 42 * @param {number=} column 43 * @param {number=} repeatCount 44 * @param {Array.<RuntimeAgent.RemoteObject>=} parameters 45 * @param {ConsoleAgent.StackTrace=} stackTrace 46 * @param {NetworkAgent.RequestId=} requestId 47 * @param {boolean=} isOutdated 48 */ 49 WebInspector.ConsoleMessageImpl = function(source, level, message, linkifier, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated) 50 { 51 WebInspector.ConsoleMessage.call(this, source, level, url, line, column, repeatCount); 52 53 this._linkifier = linkifier; 54 this.type = type || WebInspector.ConsoleMessage.MessageType.Log; 55 this._messageText = message; 56 this._parameters = parameters; 57 this._stackTrace = stackTrace; 58 this._request = requestId ? WebInspector.networkLog.requestForId(requestId) : null; 59 this._isOutdated = isOutdated; 60 /** @type {!Array.<!WebInspector.DataGrid>} */ 61 this._dataGrids = []; 62 /** @type {!Map.<!WebInspector.DataGrid, Element>} */ 63 this._dataGridParents = new Map(); 64 65 this._customFormatters = { 66 "object": this._formatParameterAsObject, 67 "array": this._formatParameterAsArray, 68 "node": this._formatParameterAsNode, 69 "string": this._formatParameterAsString 70 }; 71 } 72 73 WebInspector.ConsoleMessageImpl.prototype = { 74 request: function() 75 { 76 return this._request; 77 }, 78 79 wasShown: function() 80 { 81 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) { 82 var dataGrid = this._dataGrids[i]; 83 var parentElement = this._dataGridParents.get(dataGrid) || null; 84 dataGrid.show(parentElement); 85 } 86 }, 87 88 willHide: function() 89 { 90 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) { 91 var dataGrid = this._dataGrids[i]; 92 this._dataGridParents.put(dataGrid, dataGrid.element.parentElement); 93 dataGrid.detach(); 94 } 95 }, 96 97 _formatMessage: function() 98 { 99 this._formattedMessage = document.createElement("span"); 100 this._formattedMessage.className = "console-message-text source-code"; 101 102 if (this.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) { 103 switch (this.type) { 104 case WebInspector.ConsoleMessage.MessageType.Trace: 105 this._messageElement = this._format(this._parameters || ["console.trace()"]); 106 break; 107 case WebInspector.ConsoleMessage.MessageType.Clear: 108 this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared")); 109 this._formattedMessage.addStyleClass("console-info"); 110 break; 111 case WebInspector.ConsoleMessage.MessageType.Assert: 112 var args = [WebInspector.UIString("Assertion failed:")]; 113 if (this._parameters) 114 args = args.concat(this._parameters); 115 this._messageElement = this._format(args); 116 break; 117 case WebInspector.ConsoleMessage.MessageType.Dir: 118 var obj = this._parameters ? this._parameters[0] : undefined; 119 var args = ["%O", obj]; 120 this._messageElement = this._format(args); 121 break; 122 case WebInspector.ConsoleMessage.MessageType.Profile: 123 var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText); 124 this._messageElement = document.createTextNode(WebInspector.UIString("Profile '%s' started.", title)); 125 break; 126 case WebInspector.ConsoleMessage.MessageType.ProfileEnd: 127 var hashIndex = this._messageText.lastIndexOf("#"); 128 var title = WebInspector.ProfilesPanelDescriptor.resolveProfileTitle(this._messageText.substring(0, hashIndex)); 129 var uid = this._messageText.substring(hashIndex + 1); 130 var format = WebInspector.UIString("Profile '%s' finished.", "%_"); 131 var link = WebInspector.linkifyURLAsNode("webkit-profile://CPU/" + uid, title); 132 this._messageElement = document.createElement("span"); 133 this._formatWithSubstitutionString(format, [link], this._messageElement); 134 break; 135 default: 136 var args = this._parameters || [this._messageText]; 137 this._messageElement = this._format(args); 138 } 139 } else if (this.source === WebInspector.ConsoleMessage.MessageSource.Network) { 140 if (this._request) { 141 this._stackTrace = this._request.initiator.stackTrace; 142 if (this._request.initiator && this._request.initiator.url) { 143 this.url = this._request.initiator.url; 144 this.line = this._request.initiator.lineNumber; 145 } 146 this._messageElement = document.createElement("span"); 147 if (this.level === WebInspector.ConsoleMessage.MessageLevel.Error) { 148 this._messageElement.appendChild(document.createTextNode(this._request.requestMethod + " ")); 149 this._messageElement.appendChild(WebInspector.linkifyRequestAsNode(this._request)); 150 if (this._request.failed) 151 this._messageElement.appendChild(document.createTextNode(" " + this._request.localizedFailDescription)); 152 else 153 this._messageElement.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")")); 154 } else { 155 var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(this._messageText, WebInspector.linkifyRequestAsNode.bind(null, this._request)); 156 this._messageElement.appendChild(fragment); 157 } 158 } else { 159 if (this.url) { 160 var isExternal = !WebInspector.resourceForURL(this.url) && !WebInspector.workspace.uiSourceCodeForURL(this.url); 161 this._anchorElement = WebInspector.linkifyURLAsNode(this.url, this.url, "console-message-url", isExternal); 162 } 163 this._messageElement = this._format([this._messageText]); 164 } 165 } else { 166 var args = this._parameters || [this._messageText]; 167 this._messageElement = this._format(args); 168 } 169 170 if (this.source !== WebInspector.ConsoleMessage.MessageSource.Network || this._request) { 171 if (this._stackTrace && this._stackTrace.length && this._stackTrace[0].url) { 172 this._anchorElement = this._linkifyCallFrame(this._stackTrace[0]); 173 } else if (this.url && this.url !== "undefined") { 174 this._anchorElement = this._linkifyLocation(this.url, this.line, this.column); 175 } 176 } 177 178 this._formattedMessage.appendChild(this._messageElement); 179 if (this._anchorElement) { 180 this._formattedMessage.appendChild(document.createTextNode(" ")); 181 this._formattedMessage.appendChild(this._anchorElement); 182 } 183 184 var dumpStackTrace = !!this._stackTrace && this._stackTrace.length && (this.source === WebInspector.ConsoleMessage.MessageSource.Network || this.level === WebInspector.ConsoleMessage.MessageLevel.Error || this.type === WebInspector.ConsoleMessage.MessageType.Trace); 185 if (dumpStackTrace) { 186 var ol = document.createElement("ol"); 187 ol.className = "outline-disclosure"; 188 var treeOutline = new TreeOutline(ol); 189 190 var content = this._formattedMessage; 191 var root = new TreeElement(content, null, true); 192 content.treeElementForTest = root; 193 treeOutline.appendChild(root); 194 if (this.type === WebInspector.ConsoleMessage.MessageType.Trace) 195 root.expand(); 196 197 this._populateStackTraceTreeElement(root); 198 this._formattedMessage = ol; 199 } 200 201 // This is used for inline message bubbles in SourceFrames, or other plain-text representations. 202 this._message = this._messageElement.textContent; 203 }, 204 205 /** 206 * @return {string} 207 */ 208 get message() 209 { 210 // force message formatting 211 var formattedMessage = this.formattedMessage; 212 return this._message; 213 }, 214 215 /** 216 * @return {Element} 217 */ 218 get formattedMessage() 219 { 220 if (!this._formattedMessage) 221 this._formatMessage(); 222 return this._formattedMessage; 223 }, 224 225 /** 226 * @return {?WebInspector.NetworkRequest} 227 */ 228 request: function() 229 { 230 return this._request; 231 }, 232 233 /** 234 * @param {string} url 235 * @param {number} lineNumber 236 * @param {number} columnNumber 237 * @return {Element} 238 */ 239 _linkifyLocation: function(url, lineNumber, columnNumber) 240 { 241 // FIXME(62725): stack trace line/column numbers are one-based. 242 lineNumber = lineNumber ? lineNumber - 1 : 0; 243 columnNumber = columnNumber ? columnNumber - 1 : 0; 244 return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "console-message-url"); 245 }, 246 247 /** 248 * @param {!ConsoleAgent.CallFrame} callFrame 249 * @return {Element} 250 */ 251 _linkifyCallFrame: function(callFrame) 252 { 253 return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber); 254 }, 255 256 /** 257 * @return {boolean} 258 */ 259 isErrorOrWarning: function() 260 { 261 return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error); 262 }, 263 264 _format: function(parameters) 265 { 266 // This node is used like a Builder. Values are continually appended onto it. 267 var formattedResult = document.createElement("span"); 268 if (!parameters.length) 269 return formattedResult; 270 271 // Formatting code below assumes that parameters are all wrappers whereas frontend console 272 // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here. 273 for (var i = 0; i < parameters.length; ++i) { 274 // FIXME: Only pass runtime wrappers here. 275 if (parameters[i] instanceof WebInspector.RemoteObject) 276 continue; 277 278 if (typeof parameters[i] === "object") 279 parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]); 280 else 281 parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]); 282 } 283 284 // There can be string log and string eval result. We distinguish between them based on message type. 285 var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result; 286 287 // Multiple parameters with the first being a format string. Save unused substitutions. 288 if (shouldFormatMessage) { 289 // Multiple parameters with the first being a format string. Save unused substitutions. 290 var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult); 291 parameters = result.unusedSubstitutions; 292 if (parameters.length) 293 formattedResult.appendChild(document.createTextNode(" ")); 294 } 295 296 if (this.type === WebInspector.ConsoleMessage.MessageType.Table) { 297 formattedResult.appendChild(this._formatParameterAsTable(parameters)); 298 return formattedResult; 299 } 300 301 // Single parameter, or unused substitutions from above. 302 for (var i = 0; i < parameters.length; ++i) { 303 // Inline strings when formatting. 304 if (shouldFormatMessage && parameters[i].type === "string") 305 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i].description)); 306 else 307 formattedResult.appendChild(this._formatParameter(parameters[i], false, true)); 308 if (i < parameters.length - 1) 309 formattedResult.appendChild(document.createTextNode(" ")); 310 } 311 return formattedResult; 312 }, 313 314 /** 315 * @param {Object} output 316 * @param {boolean=} forceObjectFormat 317 * @param {boolean=} includePreview 318 */ 319 _formatParameter: function(output, forceObjectFormat, includePreview) 320 { 321 var type; 322 if (forceObjectFormat) 323 type = "object"; 324 else if (output instanceof WebInspector.RemoteObject) 325 type = output.subtype || output.type; 326 else 327 type = typeof output; 328 329 var formatter = this._customFormatters[type]; 330 if (!formatter) { 331 formatter = this._formatParameterAsValue; 332 output = output.description; 333 } 334 335 var span = document.createElement("span"); 336 span.className = "console-formatted-" + type + " source-code"; 337 formatter.call(this, output, span, includePreview); 338 return span; 339 }, 340 341 _formatParameterAsValue: function(val, elem) 342 { 343 elem.appendChild(document.createTextNode(val)); 344 }, 345 346 _formatParameterAsObject: function(obj, elem, includePreview) 347 { 348 this._formatParameterAsArrayOrObject(obj, obj.description, elem, includePreview); 349 }, 350 351 /** 352 * @param {WebInspector.RemoteObject} obj 353 * @param {string} description 354 * @param {Element} elem 355 * @param {boolean} includePreview 356 */ 357 _formatParameterAsArrayOrObject: function(obj, description, elem, includePreview) 358 { 359 var titleElement = document.createElement("span"); 360 if (description) 361 titleElement.createTextChild(description); 362 if (includePreview && obj.preview) { 363 titleElement.addStyleClass("console-object-preview"); 364 var lossless = this._appendObjectPreview(obj, description, titleElement); 365 if (lossless) { 366 elem.appendChild(titleElement); 367 return; 368 } 369 } 370 var section = new WebInspector.ObjectPropertiesSection(obj, titleElement); 371 section.enableContextMenu(); 372 elem.appendChild(section.element); 373 374 var note = section.titleElement.createChild("span", "object-info-state-note"); 375 note.title = WebInspector.UIString("Object state below is captured upon first expansion"); 376 }, 377 378 /** 379 * @param {WebInspector.RemoteObject} obj 380 * @param {string} description 381 * @param {Element} titleElement 382 * @return {boolean} true iff preview captured all information. 383 */ 384 _appendObjectPreview: function(obj, description, titleElement) 385 { 386 var preview = obj.preview; 387 var isArray = obj.subtype === "array"; 388 389 if (description) 390 titleElement.createTextChild(" "); 391 titleElement.createTextChild(isArray ? "[" : "{"); 392 for (var i = 0; i < preview.properties.length; ++i) { 393 if (i > 0) 394 titleElement.createTextChild(", "); 395 396 var property = preview.properties[i]; 397 if (!isArray || property.name != i) { 398 titleElement.createChild("span", "name").textContent = property.name; 399 titleElement.createTextChild(": "); 400 } 401 402 titleElement.appendChild(this._renderPropertyPreview(property)); 403 } 404 if (preview.overflow) 405 titleElement.createChild("span").textContent = "\u2026"; 406 titleElement.createTextChild(isArray ? "]" : "}"); 407 return preview.lossless; 408 }, 409 410 /** 411 * @param {RuntimeAgent.PropertyPreview} property 412 * @return {Element} 413 */ 414 _renderPropertyPreview: function(property) 415 { 416 var span = document.createElement("span"); 417 span.className = "console-formatted-" + property.type; 418 419 if (property.type === "function") { 420 span.textContent = "function"; 421 return span; 422 } 423 424 if (property.type === "object" && property.subtype === "regexp") { 425 span.addStyleClass("console-formatted-string"); 426 span.textContent = property.value; 427 return span; 428 } 429 430 if (property.type === "object" && property.subtype === "node" && property.value) { 431 span.addStyleClass("console-formatted-preview-node"); 432 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(span, property.value); 433 return span; 434 } 435 436 if (property.type === "string") { 437 span.textContent = "\"" + property.value + "\""; 438 return span; 439 } 440 441 span.textContent = property.value; 442 return span; 443 }, 444 445 _formatParameterAsNode: function(object, elem) 446 { 447 function printNode(nodeId) 448 { 449 if (!nodeId) { 450 // Sometimes DOM is loaded after the sync message is being formatted, so we get no 451 // nodeId here. So we fall back to object formatting here. 452 this._formatParameterAsObject(object, elem, false); 453 return; 454 } 455 var treeOutline = new WebInspector.ElementsTreeOutline(false, false, true); 456 treeOutline.setVisible(true); 457 treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId); 458 treeOutline.element.addStyleClass("outline-disclosure"); 459 if (!treeOutline.children[0].hasChildren) 460 treeOutline.element.addStyleClass("single-node"); 461 elem.appendChild(treeOutline.element); 462 treeOutline.element.treeElementForTest = treeOutline.children[0]; 463 } 464 object.pushNodeToFrontend(printNode.bind(this)); 465 }, 466 467 /** 468 * @param {WebInspector.RemoteObject} array 469 * @return {boolean} 470 */ 471 useArrayPreviewInFormatter: function(array) 472 { 473 return this.type !== WebInspector.ConsoleMessage.MessageType.DirXML && !!array.preview; 474 }, 475 476 /** 477 * @param {WebInspector.RemoteObject} array 478 * @param {Element} elem 479 */ 480 _formatParameterAsArray: function(array, elem) 481 { 482 if (this.useArrayPreviewInFormatter(array)) { 483 this._formatParameterAsArrayOrObject(array, "", elem, true); 484 return; 485 } 486 487 const maxFlatArrayLength = 100; 488 if (this._isOutdated || array.arrayLength() > maxFlatArrayLength) 489 this._formatParameterAsObject(array, elem, false); 490 else 491 array.getOwnProperties(this._printArray.bind(this, array, elem)); 492 }, 493 494 /** 495 * @param {Array.<WebInspector.RemoteObject>} parameters 496 * @return {Element} 497 */ 498 _formatParameterAsTable: function(parameters) 499 { 500 var element = document.createElement("span"); 501 var table = parameters[0]; 502 if (!table || !table.preview) 503 return element; 504 505 var columnNames = []; 506 var preview = table.preview; 507 var rows = []; 508 for (var i = 0; i < preview.properties.length; ++i) { 509 var rowProperty = preview.properties[i]; 510 var rowPreview = rowProperty.valuePreview; 511 if (!rowPreview) 512 continue; 513 514 var rowValue = {}; 515 const maxColumnsToRender = 20; 516 for (var j = 0; j < rowPreview.properties.length && j < maxColumnsToRender; ++j) { 517 var cellProperty = rowPreview.properties[j]; 518 if (columnNames.indexOf(cellProperty.name) === -1) { 519 if (columnNames.length === maxColumnsToRender) 520 continue; 521 columnNames.push(cellProperty.name); 522 } 523 rowValue[cellProperty.name] = this._renderPropertyPreview(cellProperty); 524 } 525 rows.push([rowProperty.name, rowValue]); 526 } 527 528 var flatValues = []; 529 for (var i = 0; i < rows.length; ++i) { 530 var rowName = rows[i][0]; 531 var rowValue = rows[i][1]; 532 flatValues.push(rowName); 533 for (var j = 0; j < columnNames.length; ++j) 534 flatValues.push(rowValue[columnNames[j]]); 535 } 536 537 if (!flatValues.length) 538 return element; 539 columnNames.unshift(WebInspector.UIString("(index)")); 540 var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues); 541 dataGrid.renderInline(); 542 this._dataGrids.push(dataGrid); 543 this._dataGridParents.put(dataGrid, element); 544 return element; 545 }, 546 547 _formatParameterAsString: function(output, elem) 548 { 549 var span = document.createElement("span"); 550 span.className = "console-formatted-string source-code"; 551 span.appendChild(WebInspector.linkifyStringAsFragment(output.description)); 552 553 // Make black quotes. 554 elem.removeStyleClass("console-formatted-string"); 555 elem.appendChild(document.createTextNode("\"")); 556 elem.appendChild(span); 557 elem.appendChild(document.createTextNode("\"")); 558 }, 559 560 _printArray: function(array, elem, properties) 561 { 562 if (!properties) 563 return; 564 565 var elements = []; 566 for (var i = 0; i < properties.length; ++i) { 567 var property = properties[i]; 568 var name = property.name; 569 if (!isNaN(name)) 570 elements[name] = this._formatAsArrayEntry(property.value); 571 } 572 573 elem.appendChild(document.createTextNode("[")); 574 var lastNonEmptyIndex = -1; 575 576 function appendUndefined(elem, index) 577 { 578 if (index - lastNonEmptyIndex <= 1) 579 return; 580 var span = elem.createChild("span", "console-formatted-undefined"); 581 span.textContent = WebInspector.UIString("undefined %d", index - lastNonEmptyIndex - 1); 582 } 583 584 var length = array.arrayLength(); 585 for (var i = 0; i < length; ++i) { 586 var element = elements[i]; 587 if (!element) 588 continue; 589 590 if (i - lastNonEmptyIndex > 1) { 591 appendUndefined(elem, i); 592 elem.appendChild(document.createTextNode(", ")); 593 } 594 595 elem.appendChild(element); 596 lastNonEmptyIndex = i; 597 if (i < length - 1) 598 elem.appendChild(document.createTextNode(", ")); 599 } 600 appendUndefined(elem, length); 601 602 elem.appendChild(document.createTextNode("]")); 603 }, 604 605 _formatAsArrayEntry: function(output) 606 { 607 // Prevent infinite expansion of cross-referencing arrays. 608 return this._formatParameter(output, output.subtype && output.subtype === "array", false); 609 }, 610 611 _formatWithSubstitutionString: function(format, parameters, formattedResult) 612 { 613 var formatters = {}; 614 615 function parameterFormatter(force, obj) 616 { 617 return this._formatParameter(obj, force, false); 618 } 619 620 function stringFormatter(obj) 621 { 622 return obj.description; 623 } 624 625 function floatFormatter(obj) 626 { 627 if (typeof obj.value !== "number") 628 return "NaN"; 629 return obj.value; 630 } 631 632 function integerFormatter(obj) 633 { 634 if (typeof obj.value !== "number") 635 return "NaN"; 636 return Math.floor(obj.value); 637 } 638 639 function bypassFormatter(obj) 640 { 641 return (obj instanceof Node) ? obj : ""; 642 } 643 644 var currentStyle = null; 645 function styleFormatter(obj) 646 { 647 currentStyle = {}; 648 var buffer = document.createElement("span"); 649 buffer.setAttribute("style", obj.description); 650 for (var i = 0; i < buffer.style.length; i++) { 651 var property = buffer.style[i]; 652 if (isWhitelistedProperty(property)) 653 currentStyle[property] = buffer.style[property]; 654 } 655 } 656 657 function isWhitelistedProperty(property) 658 { 659 var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"]; 660 for (var i = 0; i < prefixes.length; i++) { 661 if (property.startsWith(prefixes[i])) 662 return true; 663 } 664 return false; 665 } 666 667 // Firebug uses %o for formatting objects. 668 formatters.o = parameterFormatter.bind(this, false); 669 formatters.s = stringFormatter; 670 formatters.f = floatFormatter; 671 // Firebug allows both %i and %d for formatting integers. 672 formatters.i = integerFormatter; 673 formatters.d = integerFormatter; 674 675 // Firebug uses %c for styling the message. 676 formatters.c = styleFormatter; 677 678 // Support %O to force object formatting, instead of the type-based %o formatting. 679 formatters.O = parameterFormatter.bind(this, true); 680 681 formatters._ = bypassFormatter; 682 683 function append(a, b) 684 { 685 if (b instanceof Node) 686 a.appendChild(b); 687 else if (typeof b !== "undefined") { 688 var toAppend = WebInspector.linkifyStringAsFragment(String(b)); 689 if (currentStyle) { 690 var wrapper = document.createElement('span'); 691 for (var key in currentStyle) 692 wrapper.style[key] = currentStyle[key]; 693 wrapper.appendChild(toAppend); 694 toAppend = wrapper; 695 } 696 a.appendChild(toAppend); 697 } 698 return a; 699 } 700 701 // String.format does treat formattedResult like a Builder, result is an object. 702 return String.format(format, parameters, formatters, formattedResult, append); 703 }, 704 705 clearHighlight: function() 706 { 707 if (!this._formattedMessage) 708 return; 709 710 var highlightedMessage = this._formattedMessage; 711 delete this._formattedMessage; 712 delete this._anchorElement; 713 delete this._messageElement; 714 this._formatMessage(); 715 this._element.replaceChild(this._formattedMessage, highlightedMessage); 716 }, 717 718 highlightSearchResults: function(regexObject) 719 { 720 if (!this._formattedMessage) 721 return; 722 723 this._highlightSearchResultsInElement(regexObject, this._messageElement); 724 if (this._anchorElement) 725 this._highlightSearchResultsInElement(regexObject, this._anchorElement); 726 727 this._element.scrollIntoViewIfNeeded(); 728 }, 729 730 _highlightSearchResultsInElement: function(regexObject, element) 731 { 732 regexObject.lastIndex = 0; 733 var text = element.textContent; 734 var match = regexObject.exec(text); 735 var matchRanges = []; 736 while (match) { 737 matchRanges.push({ offset: match.index, length: match[0].length }); 738 match = regexObject.exec(text); 739 } 740 WebInspector.highlightSearchResults(element, matchRanges); 741 }, 742 743 matchesRegex: function(regexObject) 744 { 745 regexObject.lastIndex = 0; 746 return regexObject.test(this.message) || (this._anchorElement && regexObject.test(this._anchorElement.textContent)); 747 }, 748 749 toMessageElement: function() 750 { 751 if (this._element) 752 return this._element; 753 754 var element = document.createElement("div"); 755 element.message = this; 756 element.className = "console-message"; 757 758 this._element = element; 759 760 switch (this.level) { 761 case WebInspector.ConsoleMessage.MessageLevel.Log: 762 element.addStyleClass("console-log-level"); 763 break; 764 case WebInspector.ConsoleMessage.MessageLevel.Debug: 765 element.addStyleClass("console-debug-level"); 766 break; 767 case WebInspector.ConsoleMessage.MessageLevel.Warning: 768 element.addStyleClass("console-warning-level"); 769 break; 770 case WebInspector.ConsoleMessage.MessageLevel.Error: 771 element.addStyleClass("console-error-level"); 772 break; 773 } 774 775 if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) 776 element.addStyleClass("console-group-title"); 777 778 element.appendChild(this.formattedMessage); 779 780 if (this.repeatCount > 1) 781 this.updateRepeatCount(); 782 783 return element; 784 }, 785 786 _populateStackTraceTreeElement: function(parentTreeElement) 787 { 788 for (var i = 0; i < this._stackTrace.length; i++) { 789 var frame = this._stackTrace[i]; 790 791 var content = document.createElement("div"); 792 var messageTextElement = document.createElement("span"); 793 messageTextElement.className = "console-message-text source-code"; 794 var functionName = frame.functionName || WebInspector.UIString("(anonymous function)"); 795 messageTextElement.appendChild(document.createTextNode(functionName)); 796 content.appendChild(messageTextElement); 797 798 if (frame.url) { 799 content.appendChild(document.createTextNode(" ")); 800 var urlElement = this._linkifyCallFrame(frame); 801 content.appendChild(urlElement); 802 } 803 804 var treeElement = new TreeElement(content); 805 parentTreeElement.appendChild(treeElement); 806 } 807 }, 808 809 updateRepeatCount: function() { 810 if (!this._element) 811 return; 812 813 if (!this.repeatCountElement) { 814 this.repeatCountElement = document.createElement("span"); 815 this.repeatCountElement.className = "bubble"; 816 817 this._element.insertBefore(this.repeatCountElement, this._element.firstChild); 818 this._element.addStyleClass("repeated-message"); 819 } 820 this.repeatCountElement.textContent = this.repeatCount; 821 }, 822 823 toString: function() 824 { 825 var sourceString; 826 switch (this.source) { 827 case WebInspector.ConsoleMessage.MessageSource.XML: 828 sourceString = "XML"; 829 break; 830 case WebInspector.ConsoleMessage.MessageSource.JS: 831 sourceString = "JS"; 832 break; 833 case WebInspector.ConsoleMessage.MessageSource.Network: 834 sourceString = "Network"; 835 break; 836 case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI: 837 sourceString = "ConsoleAPI"; 838 break; 839 case WebInspector.ConsoleMessage.MessageSource.Storage: 840 sourceString = "Storage"; 841 break; 842 case WebInspector.ConsoleMessage.MessageSource.AppCache: 843 sourceString = "AppCache"; 844 break; 845 case WebInspector.ConsoleMessage.MessageSource.Rendering: 846 sourceString = "Rendering"; 847 break; 848 case WebInspector.ConsoleMessage.MessageSource.CSS: 849 sourceString = "CSS"; 850 break; 851 case WebInspector.ConsoleMessage.MessageSource.Security: 852 sourceString = "Security"; 853 break; 854 case WebInspector.ConsoleMessage.MessageSource.Other: 855 sourceString = "Other"; 856 break; 857 } 858 859 var typeString; 860 switch (this.type) { 861 case WebInspector.ConsoleMessage.MessageType.Log: 862 typeString = "Log"; 863 break; 864 case WebInspector.ConsoleMessage.MessageType.Dir: 865 typeString = "Dir"; 866 break; 867 case WebInspector.ConsoleMessage.MessageType.DirXML: 868 typeString = "Dir XML"; 869 break; 870 case WebInspector.ConsoleMessage.MessageType.Trace: 871 typeString = "Trace"; 872 break; 873 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed: 874 case WebInspector.ConsoleMessage.MessageType.StartGroup: 875 typeString = "Start Group"; 876 break; 877 case WebInspector.ConsoleMessage.MessageType.EndGroup: 878 typeString = "End Group"; 879 break; 880 case WebInspector.ConsoleMessage.MessageType.Assert: 881 typeString = "Assert"; 882 break; 883 case WebInspector.ConsoleMessage.MessageType.Result: 884 typeString = "Result"; 885 break; 886 case WebInspector.ConsoleMessage.MessageType.Profile: 887 case WebInspector.ConsoleMessage.MessageType.ProfileEnd: 888 typeString = "Profiling"; 889 break; 890 } 891 892 var levelString; 893 switch (this.level) { 894 case WebInspector.ConsoleMessage.MessageLevel.Log: 895 levelString = "Log"; 896 break; 897 case WebInspector.ConsoleMessage.MessageLevel.Warning: 898 levelString = "Warning"; 899 break; 900 case WebInspector.ConsoleMessage.MessageLevel.Debug: 901 levelString = "Debug"; 902 break; 903 case WebInspector.ConsoleMessage.MessageLevel.Error: 904 levelString = "Error"; 905 break; 906 } 907 908 return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line; 909 }, 910 911 get text() 912 { 913 return this._messageText; 914 }, 915 916 location: function() 917 { 918 // FIXME(62725): stack trace line/column numbers are one-based. 919 var lineNumber = this.stackTrace ? this.stackTrace[0].lineNumber - 1 : this.line - 1; 920 var columnNumber = this.stackTrace && this.stackTrace[0].columnNumber ? this.stackTrace[0].columnNumber - 1 : 0; 921 return WebInspector.debuggerModel.createRawLocationByURL(this.url, lineNumber, columnNumber); 922 }, 923 924 isEqual: function(msg) 925 { 926 if (!msg) 927 return false; 928 929 if (this._stackTrace) { 930 if (!msg._stackTrace) 931 return false; 932 var l = this._stackTrace; 933 var r = msg._stackTrace; 934 if (l.length !== r.length) 935 return false; 936 for (var i = 0; i < l.length; i++) { 937 if (l[i].url !== r[i].url || 938 l[i].functionName !== r[i].functionName || 939 l[i].lineNumber !== r[i].lineNumber || 940 l[i].columnNumber !== r[i].columnNumber) 941 return false; 942 } 943 } 944 945 return (this.source === msg.source) 946 && (this.type === msg.type) 947 && (this.level === msg.level) 948 && (this.line === msg.line) 949 && (this.url === msg.url) 950 && (this.message === msg.message) 951 && (this._request === msg._request); 952 }, 953 954 get stackTrace() 955 { 956 return this._stackTrace; 957 }, 958 959 /** 960 * @return {WebInspector.ConsoleMessage} 961 */ 962 clone: function() 963 { 964 return WebInspector.ConsoleMessage.create(this.source, this.level, this._messageText, this.type, this.url, this.line, this.column, this.repeatCount, this._parameters, this._stackTrace, this._request ? this._request.requestId : undefined, this._isOutdated); 965 }, 966 967 __proto__: WebInspector.ConsoleMessage.prototype 968 } 969