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 * @implements {WebInspector.ViewportElement}
34 * @param {!WebInspector.ConsoleMessage} consoleMessage
35 * @param {?WebInspector.Linkifier} linkifier
36 * @param {number} nestingLevel
38 WebInspector.ConsoleViewMessage = function(consoleMessage, linkifier, nestingLevel)
40 this._message = consoleMessage;
41 this._linkifier = linkifier;
42 this._repeatCount = 1;
43 this._closeGroupDecorationCount = 0;
44 this._nestingLevel = nestingLevel;
46 /** @type {!Array.<!WebInspector.DataGrid>} */
48 /** @type {!Map.<!WebInspector.DataGrid, ?Element>} */
49 this._dataGridParents = new Map();
51 /** @type {!Object.<string, function(!WebInspector.RemoteObject, !Element, boolean=)>} */
52 this._customFormatters = {
53 "object": this._formatParameterAsObject,
54 "array": this._formatParameterAsArray,
55 "node": this._formatParameterAsNode,
56 "string": this._formatParameterAsString
60 WebInspector.ConsoleViewMessage.prototype = {
62 * @return {?WebInspector.Target}
66 return this.consoleMessage().target();
74 return this.toMessageElement();
79 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
80 var dataGrid = this._dataGrids[i];
81 var parentElement = this._dataGridParents.get(dataGrid) || null;
82 dataGrid.show(parentElement);
83 dataGrid.updateWidths();
87 cacheFastHeight: function()
89 this._cachedHeight = this.contentElement().offsetHeight;
94 for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
95 var dataGrid = this._dataGrids[i];
96 this._dataGridParents.put(dataGrid, dataGrid.element.parentElement);
104 fastHeight: function()
106 if (this._cachedHeight)
107 return this._cachedHeight;
108 const defaultConsoleRowHeight = 16;
109 if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table) {
110 var table = this._message.parameters[0];
111 if (table && table.preview)
112 return defaultConsoleRowHeight * table.preview.properties.length;
114 return defaultConsoleRowHeight;
118 * @return {!WebInspector.ConsoleMessage}
120 consoleMessage: function()
122 return this._message;
125 _formatMessage: function()
127 this._formattedMessage = document.createElement("span");
128 this._formattedMessage.className = "console-message-text source-code";
131 * @param {string} title
133 * @this {WebInspector.ConsoleMessage}
135 function linkifyRequest(title)
137 return WebInspector.Linkifier.linkifyUsingRevealer(/** @type {!WebInspector.NetworkRequest} */ (this.request), title, this.url);
140 var consoleMessage = this._message;
141 if (!this._messageElement) {
142 if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) {
143 switch (consoleMessage.type) {
144 case WebInspector.ConsoleMessage.MessageType.Trace:
145 this._messageElement = this._format(consoleMessage.parameters || ["console.trace()"]);
147 case WebInspector.ConsoleMessage.MessageType.Clear:
148 this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared"));
149 this._formattedMessage.classList.add("console-info");
151 case WebInspector.ConsoleMessage.MessageType.Assert:
152 var args = [WebInspector.UIString("Assertion failed:")];
153 if (consoleMessage.parameters)
154 args = args.concat(consoleMessage.parameters);
155 this._messageElement = this._format(args);
157 case WebInspector.ConsoleMessage.MessageType.Dir:
158 var obj = consoleMessage.parameters ? consoleMessage.parameters[0] : undefined;
159 var args = ["%O", obj];
160 this._messageElement = this._format(args);
162 case WebInspector.ConsoleMessage.MessageType.Profile:
163 case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
164 this._messageElement = this._format([consoleMessage.messageText]);
167 var args = consoleMessage.parameters || [consoleMessage.messageText];
168 this._messageElement = this._format(args);
170 } else if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Network) {
171 if (consoleMessage.request) {
172 this._messageElement = document.createElement("span");
173 if (consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
174 this._messageElement.appendChild(document.createTextNode(consoleMessage.request.requestMethod + " "));
175 this._messageElement.appendChild(WebInspector.Linkifier.linkifyUsingRevealer(consoleMessage.request, consoleMessage.request.url, consoleMessage.request.url));
176 if (consoleMessage.request.failed)
177 this._messageElement.appendChild(document.createTextNode(" " + consoleMessage.request.localizedFailDescription));
179 this._messageElement.appendChild(document.createTextNode(" " + consoleMessage.request.statusCode + " (" + consoleMessage.request.statusText + ")"));
181 var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(consoleMessage.messageText, linkifyRequest.bind(consoleMessage));
182 this._messageElement.appendChild(fragment);
185 var url = consoleMessage.url;
187 var isExternal = !WebInspector.resourceForURL(url) && !WebInspector.workspace.uiSourceCodeForURL(url);
188 this._anchorElement = WebInspector.linkifyURLAsNode(url, url, "console-message-url", isExternal);
190 this._messageElement = this._format([consoleMessage.messageText]);
193 var args = consoleMessage.parameters || [consoleMessage.messageText];
194 this._messageElement = this._format(args);
198 if (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.request) {
199 var callFrame = this._callFrameAnchorFromStackTrace(consoleMessage.stackTrace);
201 this._anchorElement = this._linkifyCallFrame(callFrame);
202 else if (consoleMessage.url && consoleMessage.url !== "undefined")
203 this._anchorElement = this._linkifyLocation(consoleMessage.url, consoleMessage.line, consoleMessage.column);
206 this._formattedMessage.appendChild(this._messageElement);
207 if (this._anchorElement) {
208 this._formattedMessage.appendChild(document.createTextNode(" "));
209 this._formattedMessage.appendChild(this._anchorElement);
212 var dumpStackTrace = !!consoleMessage.stackTrace && consoleMessage.stackTrace.length && (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error || consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace);
213 if (dumpStackTrace) {
214 var ol = document.createElement("ol");
215 ol.className = "outline-disclosure";
216 var treeOutline = new TreeOutline(ol);
218 var content = this._formattedMessage;
219 var root = new TreeElement(content, null, true);
220 content.treeElementForTest = root;
221 treeOutline.appendChild(root);
222 if (consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace)
225 this._populateStackTraceTreeElement(root);
226 this._formattedMessage = ol;
230 _formattedMessageText: function()
232 this.formattedMessage();
233 return this._messageElement.textContent;
239 formattedMessage: function()
241 if (!this._formattedMessage)
242 this._formatMessage();
243 return this._formattedMessage;
247 * @param {string} url
248 * @param {number} lineNumber
249 * @param {number} columnNumber
252 _linkifyLocation: function(url, lineNumber, columnNumber)
254 console.assert(this._linkifier);
255 var target = this._target();
256 if (!this._linkifier || !target)
258 // FIXME(62725): stack trace line/column numbers are one-based.
259 lineNumber = lineNumber ? lineNumber - 1 : 0;
260 columnNumber = columnNumber ? columnNumber - 1 : 0;
261 if (this._message.source === WebInspector.ConsoleMessage.MessageSource.CSS) {
262 var headerIds = target.cssModel.styleSheetIdsForURL(url);
263 var cssLocation = new WebInspector.CSSLocation(target, url, lineNumber, columnNumber);
264 return this._linkifier.linkifyCSSLocation(headerIds[0] || null, cssLocation, "console-message-url");
267 return this._linkifier.linkifyLocation(target, url, lineNumber, columnNumber, "console-message-url");
271 * @param {!ConsoleAgent.CallFrame} callFrame
274 _linkifyCallFrame: function(callFrame)
276 console.assert(this._linkifier);
277 var target = this._target();
278 if (!this._linkifier || !target)
280 // FIXME(62725): stack trace line/column numbers are one-based.
281 var lineNumber = callFrame.lineNumber ? callFrame.lineNumber - 1 : 0;
282 var columnNumber = callFrame.columnNumber ? callFrame.columnNumber - 1 : 0;
283 var rawLocation = new WebInspector.DebuggerModel.Location(target, callFrame.scriptId, lineNumber, columnNumber);
284 return this._linkifier.linkifyRawLocation(rawLocation, "console-message-url");
288 * @param {?Array.<!ConsoleAgent.CallFrame>} stackTrace
289 * @return {?ConsoleAgent.CallFrame}
291 _callFrameAnchorFromStackTrace: function(stackTrace)
293 if (!stackTrace || !stackTrace.length)
295 var callFrame = stackTrace[0].scriptId ? stackTrace[0] : null;
296 if (!WebInspector.experimentsSettings.frameworksDebuggingSupport.isEnabled())
298 if (!WebInspector.settings.skipStackFramesSwitch.get())
300 var regex = WebInspector.settings.skipStackFramesPattern.asRegExp();
303 for (var i = 0; i < stackTrace.length; ++i) {
304 var script = this._target().debuggerModel.scriptForId(stackTrace[i].scriptId);
305 if (!script || !regex.test(script.sourceURL))
306 return stackTrace[i].scriptId ? stackTrace[i] : null;
314 isErrorOrWarning: function()
316 return (this._message.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this._message.level === WebInspector.ConsoleMessage.MessageLevel.Error);
319 _format: function(parameters)
321 // This node is used like a Builder. Values are continually appended onto it.
322 var formattedResult = document.createElement("span");
323 if (!parameters.length)
324 return formattedResult;
326 var target = this._target();
328 // Formatting code below assumes that parameters are all wrappers whereas frontend console
329 // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
330 for (var i = 0; i < parameters.length; ++i) {
331 // FIXME: Only pass runtime wrappers here.
332 if (parameters[i] instanceof WebInspector.RemoteObject)
336 parameters[i] = WebInspector.RemoteObject.fromLocalObject(parameters[i]);
340 if (typeof parameters[i] === "object")
341 parameters[i] = target.runtimeModel.createRemoteObject(parameters[i]);
343 parameters[i] = target.runtimeModel.createRemoteObjectFromPrimitiveValue(parameters[i]);
346 // There can be string log and string eval result. We distinguish between them based on message type.
347 var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this._message.type !== WebInspector.ConsoleMessage.MessageType.Result;
349 // Multiple parameters with the first being a format string. Save unused substitutions.
350 if (shouldFormatMessage) {
351 // Multiple parameters with the first being a format string. Save unused substitutions.
352 var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult);
353 parameters = result.unusedSubstitutions;
354 if (parameters.length)
355 formattedResult.appendChild(document.createTextNode(" "));
358 if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table) {
359 formattedResult.appendChild(this._formatParameterAsTable(parameters));
360 return formattedResult;
363 // Single parameter, or unused substitutions from above.
364 for (var i = 0; i < parameters.length; ++i) {
365 // Inline strings when formatting.
366 if (shouldFormatMessage && parameters[i].type === "string")
367 formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i].description));
369 formattedResult.appendChild(this._formatParameter(parameters[i], false, true));
370 if (i < parameters.length - 1)
371 formattedResult.appendChild(document.createTextNode(" "));
373 return formattedResult;
377 * @param {!WebInspector.RemoteObject} output
378 * @param {boolean=} forceObjectFormat
379 * @param {boolean=} includePreview
382 _formatParameter: function(output, forceObjectFormat, includePreview)
384 var type = forceObjectFormat ? "object" : (output.subtype || output.type);
385 var formatter = this._customFormatters[type] || this._formatParameterAsValue;
386 var span = document.createElement("span");
387 span.className = "console-formatted-" + type + " source-code";
388 formatter.call(this, output, span, includePreview);
393 * @param {!WebInspector.RemoteObject} obj
394 * @param {!Element} elem
396 _formatParameterAsValue: function(obj, elem)
398 elem.appendChild(document.createTextNode(obj.description || ""));
400 elem.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, obj), false);
404 * @param {!WebInspector.RemoteObject} obj
405 * @param {!Element} elem
406 * @param {boolean=} includePreview
408 _formatParameterAsObject: function(obj, elem, includePreview)
410 this._formatParameterAsArrayOrObject(obj, obj.description || "", elem, includePreview);
414 * @param {!WebInspector.RemoteObject} obj
415 * @param {string} description
416 * @param {!Element} elem
417 * @param {boolean=} includePreview
419 _formatParameterAsArrayOrObject: function(obj, description, elem, includePreview)
421 var titleElement = document.createElement("span");
423 titleElement.createTextChild(description);
424 if (includePreview && obj.preview) {
425 titleElement.classList.add("console-object-preview");
426 var lossless = this._appendObjectPreview(obj, description, titleElement);
428 elem.appendChild(titleElement);
429 titleElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, obj), false);
433 var section = new WebInspector.ObjectPropertiesSection(obj, titleElement);
434 section.enableContextMenu();
435 elem.appendChild(section.element);
437 var note = section.titleElement.createChild("span", "object-info-state-note");
438 note.title = WebInspector.UIString("Object state below is captured upon first expansion");
442 * @param {!WebInspector.RemoteObject} obj
443 * @param {?Event} event
445 _contextMenuEventFired: function(obj, event)
447 var contextMenu = new WebInspector.ContextMenu(event);
448 contextMenu.appendApplicableItems(obj);
453 * @param {!WebInspector.RemoteObject} obj
454 * @param {string} description
455 * @param {!Element} titleElement
456 * @return {boolean} true iff preview captured all information.
458 _appendObjectPreview: function(obj, description, titleElement)
460 var preview = obj.preview;
461 var isArray = obj.subtype === "array";
464 titleElement.createTextChild(" ");
465 titleElement.createTextChild(isArray ? "[" : "{");
466 for (var i = 0; i < preview.properties.length; ++i) {
468 titleElement.createTextChild(", ");
470 var property = preview.properties[i];
471 var name = property.name;
472 if (!isArray || name != i) {
473 if (/^\s|\s$|^$|\n/.test(name))
474 name = "\"" + name.replace(/\n/g, "\u21B5") + "\"";
475 titleElement.createChild("span", "name").textContent = name;
476 titleElement.createTextChild(": ");
479 titleElement.appendChild(this._renderPropertyPreviewOrAccessor(obj, [property]));
481 if (preview.overflow)
482 titleElement.createChild("span").textContent = "\u2026";
483 titleElement.createTextChild(isArray ? "]" : "}");
484 return preview.lossless;
488 * @param {!WebInspector.RemoteObject} object
489 * @param {!Array.<!RuntimeAgent.PropertyPreview>} propertyPath
492 _renderPropertyPreviewOrAccessor: function(object, propertyPath)
494 var property = propertyPath.peekLast();
495 if (property.type === "accessor")
496 return this._formatAsAccessorProperty(object, propertyPath.select("name"), false);
497 return this._renderPropertyPreview(property.type, /** @type {string} */ (property.subtype), property.value);
501 * @param {string} type
502 * @param {string=} subtype
503 * @param {string=} description
506 _renderPropertyPreview: function(type, subtype, description)
508 var span = document.createElement("span");
509 span.className = "console-formatted-" + type;
511 if (type === "function") {
512 span.textContent = "function";
516 if (type === "object" && subtype === "regexp") {
517 span.classList.add("console-formatted-string");
518 span.textContent = description;
522 if (type === "object" && subtype === "node" && description) {
523 span.classList.add("console-formatted-preview-node");
524 WebInspector.DOMPresentationUtils.createSpansForNodeTitle(span, description);
528 if (type === "string") {
529 span.textContent = "\"" + description.replace(/\n/g, "\u21B5") + "\"";
533 span.textContent = description;
538 * @param {!WebInspector.RemoteObject} object
539 * @param {!Element} elem
541 _formatParameterAsNode: function(object, elem)
544 * @param {!WebInspector.DOMNode} node
545 * @this {WebInspector.ConsoleViewMessage}
547 function printNode(node)
550 // Sometimes DOM is loaded after the sync message is being formatted, so we get no
551 // nodeId here. So we fall back to object formatting here.
552 this._formatParameterAsObject(object, elem, false);
555 var renderer = WebInspector.moduleManager.instance(WebInspector.Renderer, node);
557 elem.appendChild(renderer.render(node));
559 console.error("No renderer for node found");
561 object.pushNodeToFrontend(printNode.bind(this));
565 * @param {!WebInspector.RemoteObject} array
568 useArrayPreviewInFormatter: function(array)
570 return this._message.type !== WebInspector.ConsoleMessage.MessageType.DirXML && !!array.preview;
574 * @param {!WebInspector.RemoteObject} array
575 * @param {!Element} elem
577 _formatParameterAsArray: function(array, elem)
579 if (this.useArrayPreviewInFormatter(array)) {
580 this._formatParameterAsArrayOrObject(array, "", elem, true);
584 const maxFlatArrayLength = 100;
585 if (this._message.isOutdated || array.arrayLength() > maxFlatArrayLength)
586 this._formatParameterAsObject(array, elem, false);
588 array.getOwnProperties(this._printArray.bind(this, array, elem));
592 * @param {!Array.<!WebInspector.RemoteObject>} parameters
595 _formatParameterAsTable: function(parameters)
597 var element = document.createElement("span");
598 var table = parameters[0];
599 if (!table || !table.preview)
602 var columnNames = [];
603 var preview = table.preview;
605 for (var i = 0; i < preview.properties.length; ++i) {
606 var rowProperty = preview.properties[i];
607 var rowPreview = rowProperty.valuePreview;
612 const maxColumnsToRender = 20;
613 for (var j = 0; j < rowPreview.properties.length; ++j) {
614 var cellProperty = rowPreview.properties[j];
615 var columnRendered = columnNames.indexOf(cellProperty.name) != -1;
616 if (!columnRendered) {
617 if (columnNames.length === maxColumnsToRender)
619 columnRendered = true;
620 columnNames.push(cellProperty.name);
623 if (columnRendered) {
624 var cellElement = this._renderPropertyPreviewOrAccessor(table, [rowProperty, cellProperty]);
625 cellElement.classList.add("nowrap-below");
626 rowValue[cellProperty.name] = cellElement;
629 rows.push([rowProperty.name, rowValue]);
633 for (var i = 0; i < rows.length; ++i) {
634 var rowName = rows[i][0];
635 var rowValue = rows[i][1];
636 flatValues.push(rowName);
637 for (var j = 0; j < columnNames.length; ++j)
638 flatValues.push(rowValue[columnNames[j]]);
641 var dataGridContainer = element.createChild("span");
642 if (!preview.lossless || !flatValues.length) {
643 element.appendChild(this._formatParameter(table, true, false));
644 if (!flatValues.length)
648 columnNames.unshift(WebInspector.UIString("(index)"));
649 var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues);
650 dataGrid.renderInline();
651 this._dataGrids.push(dataGrid);
652 this._dataGridParents.put(dataGrid, dataGridContainer);
657 * @param {!WebInspector.RemoteObject} output
658 * @param {!Element} elem
660 _formatParameterAsString: function(output, elem)
662 var span = document.createElement("span");
663 span.className = "console-formatted-string source-code";
664 span.appendChild(WebInspector.linkifyStringAsFragment(output.description || ""));
666 // Make black quotes.
667 elem.classList.remove("console-formatted-string");
668 elem.appendChild(document.createTextNode("\""));
669 elem.appendChild(span);
670 elem.appendChild(document.createTextNode("\""));
674 * @param {!WebInspector.RemoteObject} array
675 * @param {!Element} elem
676 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
678 _printArray: function(array, elem, properties)
684 for (var i = 0; i < properties.length; ++i) {
685 var property = properties[i];
686 var name = property.name;
690 elements[name] = this._formatAsAccessorProperty(array, [name], true);
691 else if (property.value)
692 elements[name] = this._formatAsArrayEntry(property.value);
695 elem.appendChild(document.createTextNode("["));
696 var lastNonEmptyIndex = -1;
698 function appendUndefined(elem, index)
700 if (index - lastNonEmptyIndex <= 1)
702 var span = elem.createChild("span", "console-formatted-undefined");
703 span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1);
706 var length = array.arrayLength();
707 for (var i = 0; i < length; ++i) {
708 var element = elements[i];
712 if (i - lastNonEmptyIndex > 1) {
713 appendUndefined(elem, i);
714 elem.appendChild(document.createTextNode(", "));
717 elem.appendChild(element);
718 lastNonEmptyIndex = i;
720 elem.appendChild(document.createTextNode(", "));
722 appendUndefined(elem, length);
724 elem.appendChild(document.createTextNode("]"));
725 elem.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, array), false);
729 * @param {!WebInspector.RemoteObject} output
732 _formatAsArrayEntry: function(output)
734 // Prevent infinite expansion of cross-referencing arrays.
735 return this._formatParameter(output, output.subtype === "array", false);
739 * @param {!WebInspector.RemoteObject} object
740 * @param {!Array.<string>} propertyPath
741 * @param {boolean} isArrayEntry
744 _formatAsAccessorProperty: function(object, propertyPath, isArrayEntry)
746 var rootElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan(object, propertyPath, onInvokeGetterClick.bind(this));
749 * @param {?WebInspector.RemoteObject} result
750 * @param {boolean=} wasThrown
751 * @this {WebInspector.ConsoleViewMessage}
753 function onInvokeGetterClick(result, wasThrown)
757 rootElement.removeChildren();
759 var element = rootElement.createChild("span", "error-message");
760 element.textContent = WebInspector.UIString("<exception>");
761 element.title = /** @type {string} */ (result.description);
762 } else if (isArrayEntry) {
763 rootElement.appendChild(this._formatAsArrayEntry(result));
765 // Make a PropertyPreview from the RemoteObject similar to the backend logic.
766 const maxLength = 100;
767 var type = result.type;
768 var subtype = result.subtype;
769 var description = "";
770 if (type !== "function" && result.description) {
771 if (type === "string" || subtype === "regexp")
772 description = result.description.trimMiddle(maxLength);
774 description = result.description.trimEnd(maxLength);
776 rootElement.appendChild(this._renderPropertyPreview(type, subtype, description));
784 * @param {string} format
785 * @param {!Array.<string>} parameters
786 * @param {!Element} formattedResult
788 _formatWithSubstitutionString: function(format, parameters, formattedResult)
793 * @param {boolean} force
794 * @param {!WebInspector.RemoteObject} obj
796 * @this {WebInspector.ConsoleViewMessage}
798 function parameterFormatter(force, obj)
800 return this._formatParameter(obj, force, false);
803 function stringFormatter(obj)
805 return obj.description;
808 function floatFormatter(obj)
810 if (typeof obj.value !== "number")
815 function integerFormatter(obj)
817 if (typeof obj.value !== "number")
819 return Math.floor(obj.value);
822 function bypassFormatter(obj)
824 return (obj instanceof Node) ? obj : "";
827 var currentStyle = null;
828 function styleFormatter(obj)
831 var buffer = document.createElement("span");
832 buffer.setAttribute("style", obj.description);
833 for (var i = 0; i < buffer.style.length; i++) {
834 var property = buffer.style[i];
835 if (isWhitelistedProperty(property))
836 currentStyle[property] = buffer.style[property];
840 function isWhitelistedProperty(property)
842 var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"];
843 for (var i = 0; i < prefixes.length; i++) {
844 if (property.startsWith(prefixes[i]))
850 // Firebug uses %o for formatting objects.
851 formatters.o = parameterFormatter.bind(this, false);
852 formatters.s = stringFormatter;
853 formatters.f = floatFormatter;
854 // Firebug allows both %i and %d for formatting integers.
855 formatters.i = integerFormatter;
856 formatters.d = integerFormatter;
858 // Firebug uses %c for styling the message.
859 formatters.c = styleFormatter;
861 // Support %O to force object formatting, instead of the type-based %o formatting.
862 formatters.O = parameterFormatter.bind(this, true);
864 formatters._ = bypassFormatter;
866 function append(a, b)
868 if (b instanceof Node)
870 else if (typeof b !== "undefined") {
871 var toAppend = WebInspector.linkifyStringAsFragment(String(b));
873 var wrapper = document.createElement('span');
874 for (var key in currentStyle)
875 wrapper.style[key] = currentStyle[key];
876 wrapper.appendChild(toAppend);
879 a.appendChild(toAppend);
884 // String.format does treat formattedResult like a Builder, result is an object.
885 return String.format(format, parameters, formatters, formattedResult, append);
888 clearHighlight: function()
890 if (!this._formattedMessage)
893 var highlightedMessage = this._formattedMessage;
894 delete this._formattedMessage;
895 delete this._anchorElement;
896 delete this._messageElement;
897 this._formatMessage();
898 this._element.replaceChild(this._formattedMessage, highlightedMessage);
901 highlightSearchResults: function(regexObject)
903 if (!this._formattedMessage)
906 this._highlightSearchResultsInElement(regexObject, this._messageElement);
907 if (this._anchorElement)
908 this._highlightSearchResultsInElement(regexObject, this._anchorElement);
911 _highlightSearchResultsInElement: function(regexObject, element)
913 regexObject.lastIndex = 0;
914 var text = element.textContent;
915 var match = regexObject.exec(text);
916 var matchRanges = [];
918 matchRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
919 match = regexObject.exec(text);
921 WebInspector.highlightSearchResults(element, matchRanges);
927 matchesRegex: function(regexObject)
929 regexObject.lastIndex = 0;
930 return regexObject.test(this._formattedMessageText()) || (!!this._anchorElement && regexObject.test(this._anchorElement.textContent));
934 * @param {boolean} show
936 updateTimestamp: function(show)
941 if (show && !this.timestampElement) {
942 this.timestampElement = this._element.createChild("span", "console-timestamp");
943 this.timestampElement.textContent = (new Date(this._message.timestamp)).toConsoleTime();
944 var afterRepeatCountChild = this._repeatCountElement && this._repeatCountElement.nextSibling;
945 this._element.insertBefore(this.timestampElement, afterRepeatCountChild || this._element.firstChild);
949 if (!show && this.timestampElement) {
950 this.timestampElement.remove();
951 delete this.timestampElement;
958 nestingLevel: function()
960 return this._nestingLevel;
963 resetCloseGroupDecorationCount: function()
965 this._closeGroupDecorationCount = 0;
966 this._updateCloseGroupDecorations();
969 incrementCloseGroupDecorationCount: function()
971 ++this._closeGroupDecorationCount;
972 this._updateCloseGroupDecorations();
975 _updateCloseGroupDecorations: function()
977 if (!this._nestingLevelMarkers)
979 for (var i = 0, n = this._nestingLevelMarkers.length; i < n; ++i) {
980 var marker = this._nestingLevelMarkers[i];
981 marker.classList.toggle("group-closed", n - i <= this._closeGroupDecorationCount);
988 contentElement: function()
991 return this._element;
993 var element = document.createElementWithClass("div", "console-message");
994 this._element = element;
996 switch (this._message.level) {
997 case WebInspector.ConsoleMessage.MessageLevel.Log:
998 element.classList.add("console-log-level");
1000 case WebInspector.ConsoleMessage.MessageLevel.Debug:
1001 element.classList.add("console-debug-level");
1003 case WebInspector.ConsoleMessage.MessageLevel.Warning:
1004 element.classList.add("console-warning-level");
1006 case WebInspector.ConsoleMessage.MessageLevel.Error:
1007 element.classList.add("console-error-level");
1009 case WebInspector.ConsoleMessage.MessageLevel.Info:
1010 element.classList.add("console-info-level");
1014 if (this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
1015 element.classList.add("console-group-title");
1017 element.appendChild(this.formattedMessage());
1019 if (this._repeatCount > 1)
1020 this._showRepeatCountElement();
1022 this.updateTimestamp(WebInspector.settings.consoleTimestampsEnabled.get());
1024 return this._element;
1028 * @return {!Element}
1030 toMessageElement: function()
1032 if (this._wrapperElement)
1033 return this._wrapperElement;
1035 this._wrapperElement = document.createElementWithClass("div", "console-message-wrapper");
1036 this._nestingLevelMarkers = [];
1037 for (var i = 0; i < this._nestingLevel; ++i)
1038 this._nestingLevelMarkers.push(this._wrapperElement.createChild("div", "nesting-level-marker"));
1039 this._updateCloseGroupDecorations();
1040 this._wrapperElement.message = this;
1042 this._wrapperElement.appendChild(this.contentElement());
1043 return this._wrapperElement;
1046 _populateStackTraceTreeElement: function(parentTreeElement)
1048 for (var i = 0; i < this._message.stackTrace.length; i++) {
1049 var frame = this._message.stackTrace[i];
1051 var content = document.createElementWithClass("div", "stacktrace-entry");
1052 var messageTextElement = document.createElement("span");
1053 messageTextElement.className = "console-message-text source-code";
1054 var functionName = frame.functionName || WebInspector.UIString("(anonymous function)");
1055 messageTextElement.appendChild(document.createTextNode(functionName));
1056 content.appendChild(messageTextElement);
1058 if (frame.scriptId) {
1059 content.appendChild(document.createTextNode(" "));
1060 var urlElement = this._linkifyCallFrame(frame);
1063 content.appendChild(urlElement);
1066 var treeElement = new TreeElement(content);
1067 parentTreeElement.appendChild(treeElement);
1071 resetIncrementRepeatCount: function()
1073 this._repeatCount = 1;
1074 if (!this._repeatCountElement)
1077 this._repeatCountElement.remove();
1078 delete this._repeatCountElement;
1081 incrementRepeatCount: function()
1083 this._repeatCount++;
1084 this._showRepeatCountElement();
1087 _showRepeatCountElement: function()
1092 if (!this._repeatCountElement) {
1093 this._repeatCountElement = document.createElement("span");
1094 this._repeatCountElement.className = "bubble";
1096 this._element.insertBefore(this._repeatCountElement, this._element.firstChild);
1097 this._element.classList.add("repeated-message");
1099 this._repeatCountElement.textContent = this._repeatCount;
1105 toString: function()
1108 switch (this._message.source) {
1109 case WebInspector.ConsoleMessage.MessageSource.XML:
1110 sourceString = "XML";
1112 case WebInspector.ConsoleMessage.MessageSource.JS:
1113 sourceString = "JavaScript";
1115 case WebInspector.ConsoleMessage.MessageSource.Network:
1116 sourceString = "Network";
1118 case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI:
1119 sourceString = "ConsoleAPI";
1121 case WebInspector.ConsoleMessage.MessageSource.Storage:
1122 sourceString = "Storage";
1124 case WebInspector.ConsoleMessage.MessageSource.AppCache:
1125 sourceString = "AppCache";
1127 case WebInspector.ConsoleMessage.MessageSource.Rendering:
1128 sourceString = "Rendering";
1130 case WebInspector.ConsoleMessage.MessageSource.CSS:
1131 sourceString = "CSS";
1133 case WebInspector.ConsoleMessage.MessageSource.Security:
1134 sourceString = "Security";
1136 case WebInspector.ConsoleMessage.MessageSource.Other:
1137 sourceString = "Other";
1142 switch (this._message.type) {
1143 case WebInspector.ConsoleMessage.MessageType.Log:
1146 case WebInspector.ConsoleMessage.MessageType.Dir:
1149 case WebInspector.ConsoleMessage.MessageType.DirXML:
1150 typeString = "Dir XML";
1152 case WebInspector.ConsoleMessage.MessageType.Trace:
1153 typeString = "Trace";
1155 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
1156 case WebInspector.ConsoleMessage.MessageType.StartGroup:
1157 typeString = "Start Group";
1159 case WebInspector.ConsoleMessage.MessageType.EndGroup:
1160 typeString = "End Group";
1162 case WebInspector.ConsoleMessage.MessageType.Assert:
1163 typeString = "Assert";
1165 case WebInspector.ConsoleMessage.MessageType.Result:
1166 typeString = "Result";
1168 case WebInspector.ConsoleMessage.MessageType.Profile:
1169 case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
1170 typeString = "Profiling";
1175 switch (this._message.level) {
1176 case WebInspector.ConsoleMessage.MessageLevel.Log:
1177 levelString = "Log";
1179 case WebInspector.ConsoleMessage.MessageLevel.Warning:
1180 levelString = "Warning";
1182 case WebInspector.ConsoleMessage.MessageLevel.Debug:
1183 levelString = "Debug";
1185 case WebInspector.ConsoleMessage.MessageLevel.Error:
1186 levelString = "Error";
1188 case WebInspector.ConsoleMessage.MessageLevel.Info:
1189 levelString = "Info";
1193 return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage().textContent + "\n" + this._message.url + " line " + this._message.line;
1198 return this._message.messageText;
1204 * @extends {WebInspector.ConsoleViewMessage}
1205 * @param {!WebInspector.ConsoleMessage} consoleMessage
1206 * @param {?WebInspector.Linkifier} linkifier
1207 * @param {number} nestingLevel
1209 WebInspector.ConsoleGroupViewMessage = function(consoleMessage, linkifier, nestingLevel)
1211 console.assert(consoleMessage.isGroupStartMessage());
1212 WebInspector.ConsoleViewMessage.call(this, consoleMessage, linkifier, nestingLevel);
1213 this.setCollapsed(consoleMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed);
1216 WebInspector.ConsoleGroupViewMessage.prototype = {
1218 * @param {boolean} collapsed
1220 setCollapsed: function(collapsed)
1222 this._collapsed = collapsed;
1223 if (this._wrapperElement)
1224 this._wrapperElement.classList.toggle("collapsed", this._collapsed);
1230 collapsed: function()
1232 return this._collapsed;
1236 * @return {!Element}
1238 toMessageElement: function()
1240 if (!this._wrapperElement) {
1241 WebInspector.ConsoleViewMessage.prototype.toMessageElement.call(this);
1242 this._wrapperElement.classList.toggle("collapsed", this._collapsed);
1244 return this._wrapperElement;
1247 __proto__: WebInspector.ConsoleViewMessage.prototype