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
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
33 * @extends {WebInspector.ConsoleMessage}
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
49 WebInspector.ConsoleMessageImpl = function(source, level, message, linkifier, type, url, line, column, repeatCount, parameters, stackTrace, requestId, isOutdated)
51 WebInspector.ConsoleMessage.call(this, source, level, url, line, column, repeatCount, requestId);
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._isOutdated = isOutdated;
59 /** @type {!Array.<!WebInspector.DataGrid>} */
61 /** @type {!Map.<!WebInspector.DataGrid, ?Element>} */
62 this._dataGridParents = new Map();
64 this._customFormatters = {
65 "object": this._formatParameterAsObject,
66 "array": this._formatParameterAsArray,
67 "node": this._formatParameterAsNode,
68 "string": this._formatParameterAsString
72 WebInspector.ConsoleMessageImpl.prototype = {
75 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
76 var dataGrid = this._dataGrids[i];
77 var parentElement = this._dataGridParents.get(dataGrid) || null;
78 dataGrid.show(parentElement);
79 dataGrid.updateWidths();
85 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
86 var dataGrid = this._dataGrids[i];
87 this._dataGridParents.put(dataGrid, dataGrid.element.parentElement);
94 * @param {!Node} messageElement
96 setMessageElement: function(messageElement)
98 this._messageElement = messageElement;
101 _formatMessage: function()
103 this._formattedMessage = document.createElement("span");
104 this._formattedMessage.className = "console-message-text source-code";
107 * @param {string} title
109 * @this {WebInspector.NetworkRequest}
111 function linkifyRequest(title)
113 return WebInspector.Linkifier.linkifyUsingRevealer(this, title, this.url);
116 if (!this._messageElement) {
117 if (this.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) {
119 case WebInspector.ConsoleMessage.MessageType.Trace:
120 this._messageElement = this._format(this._parameters || ["console.trace()"]);
122 case WebInspector.ConsoleMessage.MessageType.Clear:
123 this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared"));
124 this._formattedMessage.classList.add("console-info");
126 case WebInspector.ConsoleMessage.MessageType.Assert:
127 var args = [WebInspector.UIString("Assertion failed:")];
128 if (this._parameters)
129 args = args.concat(this._parameters);
130 this._messageElement = this._format(args);
132 case WebInspector.ConsoleMessage.MessageType.Dir:
133 var obj = this._parameters ? this._parameters[0] : undefined;
134 var args = ["%O", obj];
135 this._messageElement = this._format(args);
137 case WebInspector.ConsoleMessage.MessageType.Profile:
138 case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
139 console.assert(false);
142 var args = this._parameters || [this._messageText];
143 this._messageElement = this._format(args);
145 } else if (this.source === WebInspector.ConsoleMessage.MessageSource.Network) {
147 this._stackTrace = this._request.initiator.stackTrace;
148 if (this._request.initiator && this._request.initiator.url) {
149 this.url = this._request.initiator.url;
150 this.line = this._request.initiator.lineNumber;
152 this._messageElement = document.createElement("span");
153 if (this.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
154 this._messageElement.appendChild(document.createTextNode(this._request.requestMethod + " "));
155 this._messageElement.appendChild(WebInspector.Linkifier.linkifyUsingRevealer(this._request, this._request.url, this._request.url));
156 if (this._request.failed)
157 this._messageElement.appendChild(document.createTextNode(" " + this._request.localizedFailDescription));
159 this._messageElement.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")"));
161 var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(this._messageText, linkifyRequest.bind(this._request));
162 this._messageElement.appendChild(fragment);
166 var isExternal = !WebInspector.resourceForURL(this.url) && !WebInspector.workspace.uiSourceCodeForURL(this.url);
167 this._anchorElement = WebInspector.linkifyURLAsNode(this.url, this.url, "console-message-url", isExternal);
169 this._messageElement = this._format([this._messageText]);
172 var args = this._parameters || [this._messageText];
173 this._messageElement = this._format(args);
177 if (this.source !== WebInspector.ConsoleMessage.MessageSource.Network || this._request) {
178 if (this._stackTrace && this._stackTrace.length && this._stackTrace[0].scriptId) {
179 this._anchorElement = this._linkifyCallFrame(this._stackTrace[0]);
180 } else if (this.url && this.url !== "undefined") {
181 this._anchorElement = this._linkifyLocation(this.url, this.line, this.column);
185 this._formattedMessage.appendChild(this._messageElement);
186 if (this._anchorElement) {
187 this._formattedMessage.appendChild(document.createTextNode(" "));
188 this._formattedMessage.appendChild(this._anchorElement);
191 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);
192 if (dumpStackTrace) {
193 var ol = document.createElement("ol");
194 ol.className = "outline-disclosure";
195 var treeOutline = new TreeOutline(ol);
197 var content = this._formattedMessage;
198 var root = new TreeElement(content, null, true);
199 content.treeElementForTest = root;
200 treeOutline.appendChild(root);
201 if (this.type === WebInspector.ConsoleMessage.MessageType.Trace)
204 this._populateStackTraceTreeElement(root);
205 this._formattedMessage = ol;
208 // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
209 this._message = this._messageElement.textContent;
217 // force message formatting
218 var formattedMessage = this.formattedMessage;
219 return this._message;
225 get formattedMessage()
227 if (!this._formattedMessage)
228 this._formatMessage();
229 return this._formattedMessage;
233 * @param {string} url
234 * @param {number} lineNumber
235 * @param {number} columnNumber
238 _linkifyLocation: function(url, lineNumber, columnNumber)
240 // FIXME(62725): stack trace line/column numbers are one-based.
241 lineNumber = lineNumber ? lineNumber - 1 : 0;
242 columnNumber = columnNumber ? columnNumber - 1 : 0;
243 if (this.source === WebInspector.ConsoleMessage.MessageSource.CSS) {
244 var headerIds = WebInspector.cssModel.styleSheetIdsForURL(url);
245 var cssLocation = new WebInspector.CSSLocation(url, lineNumber, columnNumber);
246 return this._linkifier.linkifyCSSLocation(headerIds[0] || null, cssLocation, "console-message-url");
249 return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
253 * @param {!ConsoleAgent.CallFrame} callFrame
256 _linkifyCallFrame: function(callFrame)
258 // FIXME(62725): stack trace line/column numbers are one-based.
259 var lineNumber = callFrame.lineNumber ? callFrame.lineNumber - 1 : 0;
260 var columnNumber = callFrame.columnNumber ? callFrame.columnNumber - 1 : 0;
261 var rawLocation = new WebInspector.DebuggerModel.Location(callFrame.scriptId, lineNumber, columnNumber);
262 return this._linkifier.linkifyRawLocation(rawLocation, "console-message-url");
268 isErrorOrWarning: function()
270 return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
273 _format: function(parameters)
275 // This node is used like a Builder. Values are continually appended onto it.
276 var formattedResult = document.createElement("span");
277 if (!parameters.length)
278 return formattedResult;
280 // Formatting code below assumes that parameters are all wrappers whereas frontend console
281 // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
282 for (var i = 0; i < parameters.length; ++i) {
283 // FIXME: Only pass runtime wrappers here.
284 if (parameters[i] instanceof WebInspector.RemoteObject)
287 if (typeof parameters[i] === "object")
288 parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]);
290 parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]);
293 // There can be string log and string eval result. We distinguish between them based on message type.
294 var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result;
296 // Multiple parameters with the first being a format string. Save unused substitutions.
297 if (shouldFormatMessage) {
298 // Multiple parameters with the first being a format string. Save unused substitutions.
299 var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult);
300 parameters = result.unusedSubstitutions;
301 if (parameters.length)
302 formattedResult.appendChild(document.createTextNode(" "));
305 if (this.type === WebInspector.ConsoleMessage.MessageType.Table) {
306 formattedResult.appendChild(this._formatParameterAsTable(parameters));
307 return formattedResult;
310 // Single parameter, or unused substitutions from above.
311 for (var i = 0; i < parameters.length; ++i) {
312 // Inline strings when formatting.
313 if (shouldFormatMessage && parameters[i].type === "string")
314 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i].description));
316 formattedResult.appendChild(this._formatParameter(parameters[i], false, true));
317 if (i < parameters.length - 1)
318 formattedResult.appendChild(document.createTextNode(" "));
320 return formattedResult;
324 * @param {?Object} output
325 * @param {boolean=} forceObjectFormat
326 * @param {boolean=} includePreview
329 _formatParameter: function(output, forceObjectFormat, includePreview)
332 if (forceObjectFormat)
334 else if (output instanceof WebInspector.RemoteObject)
335 type = output.subtype || output.type;
337 type = typeof output;
339 var formatter = this._customFormatters[type];
341 formatter = this._formatParameterAsValue;
342 output = output.description;
345 var span = document.createElement("span");
346 span.className = "console-formatted-" + type + " source-code";
347 formatter.call(this, output, span, includePreview);
351 _formatParameterAsValue: function(val, elem)
353 elem.appendChild(document.createTextNode(val));
357 * @param {!WebInspector.RemoteObject} obj
358 * @param {!Element} elem
359 * @param {boolean} includePreview
361 _formatParameterAsObject: function(obj, elem, includePreview)
363 this._formatParameterAsArrayOrObject(obj, obj.description || "", elem, includePreview);
367 * @param {!WebInspector.RemoteObject} obj
368 * @param {string} description
369 * @param {!Element} elem
370 * @param {boolean} includePreview
372 _formatParameterAsArrayOrObject: function(obj, description, elem, includePreview)
374 var titleElement = document.createElement("span");
376 titleElement.createTextChild(description);
377 if (includePreview && obj.preview) {
378 titleElement.classList.add("console-object-preview");
379 var lossless = this._appendObjectPreview(obj, description, titleElement);
381 elem.appendChild(titleElement);
385 var section = new WebInspector.ObjectPropertiesSection(obj, titleElement);
386 section.enableContextMenu();
387 elem.appendChild(section.element);
389 var note = section.titleElement.createChild("span", "object-info-state-note");
390 note.title = WebInspector.UIString("Object state below is captured upon first expansion");
394 * @param {!WebInspector.RemoteObject} obj
395 * @param {string} description
396 * @param {!Element} titleElement
397 * @return {boolean} true iff preview captured all information.
399 _appendObjectPreview: function(obj, description, titleElement)
401 var preview = obj.preview;
402 var isArray = obj.subtype === "array";
405 titleElement.createTextChild(" ");
406 titleElement.createTextChild(isArray ? "[" : "{");
407 for (var i = 0; i < preview.properties.length; ++i) {
409 titleElement.createTextChild(", ");
411 var property = preview.properties[i];
412 var name = property.name;
413 if (!isArray || name != i) {
414 if (/^\s|\s$|^$|\n/.test(name))
415 name = "\"" + name.replace(/\n/g, "\u21B5") + "\"";
416 titleElement.createChild("span", "name").textContent = name;
417 titleElement.createTextChild(": ");
420 titleElement.appendChild(this._renderPropertyPreviewOrAccessor(obj, [property]));
422 if (preview.overflow)
423 titleElement.createChild("span").textContent = "\u2026";
424 titleElement.createTextChild(isArray ? "]" : "}");
425 return preview.lossless;
429 * @param {!WebInspector.RemoteObject} object
430 * @param {!Array.<!RuntimeAgent.PropertyPreview>} propertyPath
433 _renderPropertyPreviewOrAccessor: function(object, propertyPath)
435 var property = propertyPath.peekLast();
436 if (property.type === "accessor")
437 return this._formatAsAccessorProperty(object, propertyPath.select("name"), false);
438 return this._renderPropertyPreview(property.type, /** @type {string} */ (property.subtype), property.value);
442 * @param {string} type
443 * @param {string=} subtype
444 * @param {string=} description
447 _renderPropertyPreview: function(type, subtype, description)
449 var span = document.createElement("span");
450 span.className = "console-formatted-" + type;
452 if (type === "function") {
453 span.textContent = "function";
457 if (type === "object" && subtype === "regexp") {
458 span.classList.add("console-formatted-string");
459 span.textContent = description;
463 if (type === "object" && subtype === "node" && description) {
464 span.classList.add("console-formatted-preview-node");
465 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(span, description);
469 if (type === "string") {
470 span.textContent = "\"" + description.replace(/\n/g, "\u21B5") + "\"";
474 span.textContent = description;
478 _formatParameterAsNode: function(object, elem)
481 * @param {!DOMAgent.NodeId} nodeId
482 * @this {WebInspector.ConsoleMessageImpl}
484 function printNode(nodeId)
487 // Sometimes DOM is loaded after the sync message is being formatted, so we get no
488 // nodeId here. So we fall back to object formatting here.
489 this._formatParameterAsObject(object, elem, false);
492 var node = WebInspector.domAgent.nodeForId(nodeId);
493 var renderer = WebInspector.moduleManager.instance(WebInspector.Renderer, node);
495 elem.appendChild(renderer.render(node));
497 console.error("No renderer for node found");
499 object.pushNodeToFrontend(printNode.bind(this));
503 * @param {!WebInspector.RemoteObject} array
506 useArrayPreviewInFormatter: function(array)
508 return this.type !== WebInspector.ConsoleMessage.MessageType.DirXML && !!array.preview;
512 * @param {!WebInspector.RemoteObject} array
513 * @param {!Element} elem
515 _formatParameterAsArray: function(array, elem)
517 if (this.useArrayPreviewInFormatter(array)) {
518 this._formatParameterAsArrayOrObject(array, "", elem, true);
522 const maxFlatArrayLength = 100;
523 if (this._isOutdated || array.arrayLength() > maxFlatArrayLength)
524 this._formatParameterAsObject(array, elem, false);
526 array.getOwnProperties(this._printArray.bind(this, array, elem));
530 * @param {!Array.<!WebInspector.RemoteObject>} parameters
533 _formatParameterAsTable: function(parameters)
535 var element = document.createElement("span");
536 var table = parameters[0];
537 if (!table || !table.preview)
540 var columnNames = [];
541 var preview = table.preview;
543 for (var i = 0; i < preview.properties.length; ++i) {
544 var rowProperty = preview.properties[i];
545 var rowPreview = rowProperty.valuePreview;
550 const maxColumnsToRender = 20;
551 for (var j = 0; j < rowPreview.properties.length; ++j) {
552 var cellProperty = rowPreview.properties[j];
553 var columnRendered = columnNames.indexOf(cellProperty.name) != -1;
554 if (!columnRendered) {
555 if (columnNames.length === maxColumnsToRender)
557 columnRendered = true;
558 columnNames.push(cellProperty.name);
561 if (columnRendered) {
562 var cellElement = this._renderPropertyPreviewOrAccessor(table, [rowProperty, cellProperty]);
563 cellElement.classList.add("nowrap-below");
564 rowValue[cellProperty.name] = cellElement;
567 rows.push([rowProperty.name, rowValue]);
571 for (var i = 0; i < rows.length; ++i) {
572 var rowName = rows[i][0];
573 var rowValue = rows[i][1];
574 flatValues.push(rowName);
575 for (var j = 0; j < columnNames.length; ++j)
576 flatValues.push(rowValue[columnNames[j]]);
579 if (!flatValues.length)
581 columnNames.unshift(WebInspector.UIString("(index)"));
582 var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues);
583 dataGrid.renderInline();
584 this._dataGrids.push(dataGrid);
585 this._dataGridParents.put(dataGrid, element);
589 _formatParameterAsString: function(output, elem)
591 var span = document.createElement("span");
592 span.className = "console-formatted-string source-code";
593 span.appendChild(WebInspector.linkifyStringAsFragment(output.description));
595 // Make black quotes.
596 elem.classList.remove("console-formatted-string");
597 elem.appendChild(document.createTextNode("\""));
598 elem.appendChild(span);
599 elem.appendChild(document.createTextNode("\""));
603 * @param {!WebInspector.RemoteObject} array
604 * @param {!Element} elem
605 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
607 _printArray: function(array, elem, properties)
613 for (var i = 0; i < properties.length; ++i) {
614 var property = properties[i];
615 var name = property.name;
619 elements[name] = this._formatAsAccessorProperty(array, [name], true);
620 else if (property.value)
621 elements[name] = this._formatAsArrayEntry(property.value);
624 elem.appendChild(document.createTextNode("["));
625 var lastNonEmptyIndex = -1;
627 function appendUndefined(elem, index)
629 if (index - lastNonEmptyIndex <= 1)
631 var span = elem.createChild("span", "console-formatted-undefined");
632 span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1);
635 var length = array.arrayLength();
636 for (var i = 0; i < length; ++i) {
637 var element = elements[i];
641 if (i - lastNonEmptyIndex > 1) {
642 appendUndefined(elem, i);
643 elem.appendChild(document.createTextNode(", "));
646 elem.appendChild(element);
647 lastNonEmptyIndex = i;
649 elem.appendChild(document.createTextNode(", "));
651 appendUndefined(elem, length);
653 elem.appendChild(document.createTextNode("]"));
657 * @param {!WebInspector.RemoteObject} output
660 _formatAsArrayEntry: function(output)
662 // Prevent infinite expansion of cross-referencing arrays.
663 return this._formatParameter(output, output.subtype === "array", false);
667 * @param {!WebInspector.RemoteObject} object
668 * @param {!Array.<string>} propertyPath
669 * @param {boolean} isArrayEntry
672 _formatAsAccessorProperty: function(object, propertyPath, isArrayEntry)
674 var rootElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan(object, propertyPath, onInvokeGetterClick.bind(this));
677 * @param {?WebInspector.RemoteObject} result
678 * @param {boolean=} wasThrown
679 * @this {WebInspector.ConsoleMessageImpl}
681 function onInvokeGetterClick(result, wasThrown)
685 rootElement.removeChildren();
687 var element = rootElement.createChild("span", "error-message");
688 element.textContent = WebInspector.UIString("<exception>");
689 element.title = result.description;
690 } else if (isArrayEntry) {
691 rootElement.appendChild(this._formatAsArrayEntry(result));
693 // Make a PropertyPreview from the RemoteObject similar to the backend logic.
694 const maxLength = 100;
695 var type = result.type;
696 var subtype = result.subtype;
697 var description = "";
698 if (type !== "function" && result.description) {
699 if (type === "string" || subtype === "regexp")
700 description = result.description.trimMiddle(maxLength);
702 description = result.description.trimEnd(maxLength);
704 rootElement.appendChild(this._renderPropertyPreview(type, subtype, description));
712 * @param {string} format
713 * @param {!Array.<string>} parameters
714 * @param {!Element} formattedResult
715 * @this {WebInspector.ConsoleMessageImpl}
717 _formatWithSubstitutionString: function(format, parameters, formattedResult)
722 * @param {boolean} force
723 * @param {!Object} obj
725 * @this {WebInspector.ConsoleMessageImpl}
727 function parameterFormatter(force, obj)
729 return this._formatParameter(obj, force, false);
732 function stringFormatter(obj)
734 return obj.description;
737 function floatFormatter(obj)
739 if (typeof obj.value !== "number")
744 function integerFormatter(obj)
746 if (typeof obj.value !== "number")
748 return Math.floor(obj.value);
751 function bypassFormatter(obj)
753 return (obj instanceof Node) ? obj : "";
756 var currentStyle = null;
757 function styleFormatter(obj)
760 var buffer = document.createElement("span");
761 buffer.setAttribute("style", obj.description);
762 for (var i = 0; i < buffer.style.length; i++) {
763 var property = buffer.style[i];
764 if (isWhitelistedProperty(property))
765 currentStyle[property] = buffer.style[property];
769 function isWhitelistedProperty(property)
771 var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"];
772 for (var i = 0; i < prefixes.length; i++) {
773 if (property.startsWith(prefixes[i]))
779 // Firebug uses %o for formatting objects.
780 formatters.o = parameterFormatter.bind(this, false);
781 formatters.s = stringFormatter;
782 formatters.f = floatFormatter;
783 // Firebug allows both %i and %d for formatting integers.
784 formatters.i = integerFormatter;
785 formatters.d = integerFormatter;
787 // Firebug uses %c for styling the message.
788 formatters.c = styleFormatter;
790 // Support %O to force object formatting, instead of the type-based %o formatting.
791 formatters.O = parameterFormatter.bind(this, true);
793 formatters._ = bypassFormatter;
795 function append(a, b)
797 if (b instanceof Node)
799 else if (typeof b !== "undefined") {
800 var toAppend = WebInspector.linkifyStringAsFragment(String(b));
802 var wrapper = document.createElement('span');
803 for (var key in currentStyle)
804 wrapper.style[key] = currentStyle[key];
805 wrapper.appendChild(toAppend);
808 a.appendChild(toAppend);
813 // String.format does treat formattedResult like a Builder, result is an object.
814 return String.format(format, parameters, formatters, formattedResult, append);
817 clearHighlight: function()
819 if (!this._formattedMessage)
822 var highlightedMessage = this._formattedMessage;
823 delete this._formattedMessage;
824 delete this._anchorElement;
825 delete this._messageElement;
826 this._formatMessage();
827 this._element.replaceChild(this._formattedMessage, highlightedMessage);
830 highlightSearchResults: function(regexObject)
832 if (!this._formattedMessage)
835 this._highlightSearchResultsInElement(regexObject, this._messageElement);
836 if (this._anchorElement)
837 this._highlightSearchResultsInElement(regexObject, this._anchorElement);
839 this._element.scrollIntoViewIfNeeded();
842 _highlightSearchResultsInElement: function(regexObject, element)
844 regexObject.lastIndex = 0;
845 var text = element.textContent;
846 var match = regexObject.exec(text);
847 var matchRanges = [];
849 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
850 match = regexObject.exec(text);
852 WebInspector.highlightSearchResults(element, matchRanges);
858 matchesRegex: function(regexObject)
860 regexObject.lastIndex = 0;
861 return regexObject.test(this.message) || (!!this._anchorElement && regexObject.test(this._anchorElement.textContent));
867 toMessageElement: function()
870 return this._element;
872 var element = document.createElement("div");
873 element.message = this;
874 element.className = "console-message";
876 this._element = element;
878 switch (this.level) {
879 case WebInspector.ConsoleMessage.MessageLevel.Log:
880 element.classList.add("console-log-level");
882 case WebInspector.ConsoleMessage.MessageLevel.Debug:
883 element.classList.add("console-debug-level");
885 case WebInspector.ConsoleMessage.MessageLevel.Warning:
886 element.classList.add("console-warning-level");
888 case WebInspector.ConsoleMessage.MessageLevel.Error:
889 element.classList.add("console-error-level");
891 case WebInspector.ConsoleMessage.MessageLevel.Info:
892 element.classList.add("console-info-level");
896 if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
897 element.classList.add("console-group-title");
899 element.appendChild(this.formattedMessage);
901 if (this.repeatCount > 1)
902 this.updateRepeatCount();
907 _populateStackTraceTreeElement: function(parentTreeElement)
909 for (var i = 0; i < this._stackTrace.length; i++) {
910 var frame = this._stackTrace[i];
912 var content = document.createElementWithClass("div", "stacktrace-entry");
913 var messageTextElement = document.createElement("span");
914 messageTextElement.className = "console-message-text source-code";
915 var functionName = frame.functionName || WebInspector.UIString("(anonymous function)");
916 messageTextElement.appendChild(document.createTextNode(functionName));
917 content.appendChild(messageTextElement);
919 if (frame.scriptId) {
920 content.appendChild(document.createTextNode(" "));
921 var urlElement = this._linkifyCallFrame(frame);
924 content.appendChild(urlElement);
927 var treeElement = new TreeElement(content);
928 parentTreeElement.appendChild(treeElement);
932 updateRepeatCount: function() {
936 if (!this.repeatCountElement) {
937 this.repeatCountElement = document.createElement("span");
938 this.repeatCountElement.className = "bubble";
940 this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
941 this._element.classList.add("repeated-message");
943 this.repeatCountElement.textContent = this.repeatCount;
952 switch (this.source) {
953 case WebInspector.ConsoleMessage.MessageSource.XML:
954 sourceString = "XML";
956 case WebInspector.ConsoleMessage.MessageSource.JS:
957 sourceString = "JavaScript";
959 case WebInspector.ConsoleMessage.MessageSource.Network:
960 sourceString = "Network";
962 case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI:
963 sourceString = "ConsoleAPI";
965 case WebInspector.ConsoleMessage.MessageSource.Storage:
966 sourceString = "Storage";
968 case WebInspector.ConsoleMessage.MessageSource.AppCache:
969 sourceString = "AppCache";
971 case WebInspector.ConsoleMessage.MessageSource.Rendering:
972 sourceString = "Rendering";
974 case WebInspector.ConsoleMessage.MessageSource.CSS:
975 sourceString = "CSS";
977 case WebInspector.ConsoleMessage.MessageSource.Security:
978 sourceString = "Security";
980 case WebInspector.ConsoleMessage.MessageSource.Other:
981 sourceString = "Other";
987 case WebInspector.ConsoleMessage.MessageType.Log:
990 case WebInspector.ConsoleMessage.MessageType.Dir:
993 case WebInspector.ConsoleMessage.MessageType.DirXML:
994 typeString = "Dir XML";
996 case WebInspector.ConsoleMessage.MessageType.Trace:
997 typeString = "Trace";
999 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
1000 case WebInspector.ConsoleMessage.MessageType.StartGroup:
1001 typeString = "Start Group";
1003 case WebInspector.ConsoleMessage.MessageType.EndGroup:
1004 typeString = "End Group";
1006 case WebInspector.ConsoleMessage.MessageType.Assert:
1007 typeString = "Assert";
1009 case WebInspector.ConsoleMessage.MessageType.Result:
1010 typeString = "Result";
1012 case WebInspector.ConsoleMessage.MessageType.Profile:
1013 case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
1014 typeString = "Profiling";
1019 switch (this.level) {
1020 case WebInspector.ConsoleMessage.MessageLevel.Log:
1021 levelString = "Log";
1023 case WebInspector.ConsoleMessage.MessageLevel.Warning:
1024 levelString = "Warning";
1026 case WebInspector.ConsoleMessage.MessageLevel.Debug:
1027 levelString = "Debug";
1029 case WebInspector.ConsoleMessage.MessageLevel.Error:
1030 levelString = "Error";
1032 case WebInspector.ConsoleMessage.MessageLevel.Info:
1033 levelString = "Info";
1037 return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage.textContent + "\n" + this.url + " line " + this.line;
1042 return this._messageText;
1046 * @return {?WebInspector.DebuggerModel.Location}
1048 location: function()
1050 // FIXME(62725): stack trace line/column numbers are one-based.
1051 var lineNumber = this.stackTrace ? this.stackTrace[0].lineNumber - 1 : this.line - 1;
1052 var columnNumber = this.stackTrace && this.stackTrace[0].columnNumber ? this.stackTrace[0].columnNumber - 1 : 0;
1053 return WebInspector.debuggerModel.createRawLocationByURL(this.url, lineNumber, columnNumber);
1057 * @param {?WebInspector.ConsoleMessage} msg
1060 isEqual: function(msg)
1065 if (this._stackTrace) {
1066 if (!msg._stackTrace)
1068 var l = this._stackTrace;
1069 var r = msg._stackTrace;
1070 if (l.length !== r.length)
1072 for (var i = 0; i < l.length; i++) {
1073 if (l[i].url !== r[i].url ||
1074 l[i].functionName !== r[i].functionName ||
1075 l[i].lineNumber !== r[i].lineNumber ||
1076 l[i].columnNumber !== r[i].columnNumber)
1081 return (this.source === msg.source)
1082 && (this.type === msg.type)
1083 && (this.level === msg.level)
1084 && (this.line === msg.line)
1085 && (this.url === msg.url)
1086 && (this.message === msg.message)
1087 && (this._request === msg._request);
1092 return this._stackTrace;
1096 * @return {!WebInspector.ConsoleMessage}
1100 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);
1103 __proto__: WebInspector.ConsoleMessage.prototype