diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js | 871 |
1 files changed, 871 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js b/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js new file mode 100644 index 000000000..0fa66f610 --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js @@ -0,0 +1,871 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2009 Joseph Pecoraro + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.ConsoleMessageView = class ConsoleMessageView extends WebInspector.Object +{ + constructor(message) + { + super(); + + console.assert(message instanceof WebInspector.ConsoleMessage); + + this._message = message; + this._expandable = false; + this._repeatCount = message._repeatCount || 0; + + // These are the parameters unused by the messages's optional format string. + // Any extra parameters will be displayed as children of this message. + this._extraParameters = message.parameters; + } + + // Public + + render() + { + this._element = document.createElement("div"); + this._element.classList.add("console-message"); + + // FIXME: <https://webkit.org/b/143545> Web Inspector: LogContentView should use higher level objects + this._element.__message = this._message; + this._element.__messageView = this; + + if (this._message.type === WebInspector.ConsoleMessage.MessageType.Result) { + this._element.classList.add("console-user-command-result"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Output: ")); + } else if (this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) + this._element.classList.add("console-group-title"); + + switch (this._message.level) { + case WebInspector.ConsoleMessage.MessageLevel.Log: + this._element.classList.add("console-log-level"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Log: ")); + break; + case WebInspector.ConsoleMessage.MessageLevel.Info: + this._element.classList.add("console-info-level"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Info: ")); + break; + case WebInspector.ConsoleMessage.MessageLevel.Debug: + this._element.classList.add("console-debug-level"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Debug: ")); + break; + case WebInspector.ConsoleMessage.MessageLevel.Warning: + this._element.classList.add("console-warning-level"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Warning: ")); + break; + case WebInspector.ConsoleMessage.MessageLevel.Error: + this._element.classList.add("console-error-level"); + this._element.setAttribute("data-labelprefix", WebInspector.UIString("Error: ")); + break; + } + + // FIXME: The location link should include stack trace information. + this._appendLocationLink(); + + this._messageTextElement = this._element.appendChild(document.createElement("span")); + this._messageTextElement.classList.add("console-top-level-message"); + this._messageTextElement.classList.add("console-message-text"); + this._appendMessageTextAndArguments(this._messageTextElement); + this._appendSavedResultIndex(); + + this._appendExtraParameters(); + this._appendStackTrace(); + + this._renderRepeatCount(); + } + + get element() + { + return this._element; + } + + get message() + { + return this._message; + } + + get repeatCount() + { + return this._repeatCount; + } + + set repeatCount(count) + { + console.assert(typeof count === "number"); + + if (this._repeatCount === count) + return; + + this._repeatCount = count; + + if (this._element) + this._renderRepeatCount(); + } + + _renderRepeatCount() + { + let count = this._repeatCount; + + if (count <= 1) { + if (this._repeatCountElement) { + this._repeatCountElement.remove(); + this._repeatCountElement = null; + } + return; + } + + if (!this._repeatCountElement) { + this._repeatCountElement = document.createElement("span"); + this._repeatCountElement.classList.add("repeat-count"); + this._element.insertBefore(this._repeatCountElement, this._element.firstChild); + } + + this._repeatCountElement.textContent = count; + } + + get expandable() + { + // There are extra arguments or a call stack that can be shown. + if (this._expandable) + return true; + + // There is an object tree that could be expanded. + if (this._objectTree) + return true; + + return false; + } + + expand() + { + if (this._expandable) + this._element.classList.add("expanded"); + + // Auto-expand an inner object tree if there is a single object. + // For Trace messages we are auto-expanding for the call stack, don't also auto-expand an object as well. + if (this._objectTree && this._message.type !== WebInspector.ConsoleMessage.MessageType.Trace) { + if (!this._extraParameters || this._extraParameters.length <= 1) + this._objectTree.expand(); + } + } + + collapse() + { + if (this._expandable) + this._element.classList.remove("expanded"); + + // Collapse the object tree just in cases where it was autoexpanded. + if (this._objectTree) { + if (!this._extraParameters || this._extraParameters.length <= 1) + this._objectTree.collapse(); + } + } + + toggle() + { + if (this._element.classList.contains("expanded")) + this.collapse(); + else + this.expand(); + } + + toClipboardString(isPrefixOptional) + { + let clipboardString = this._messageTextElement.innerText.removeWordBreakCharacters(); + if (this._message.savedResultIndex) + clipboardString = clipboardString.replace(/\s*=\s*(\$\d+)$/, ""); + + let hasStackTrace = this._shouldShowStackTrace(); + if (!hasStackTrace) { + let repeatString = this.repeatCount > 1 ? "x" + this.repeatCount : ""; + let urlLine = ""; + if (this._message.url) { + let components = [WebInspector.displayNameForURL(this._message.url), "line " + this._message.line]; + if (repeatString) + components.push(repeatString); + urlLine = " (" + components.join(", ") + ")"; + } else if (repeatString) + urlLine = " (" + repeatString + ")"; + + if (urlLine) { + let lines = clipboardString.split("\n"); + lines[0] += urlLine; + clipboardString = lines.join("\n"); + } + } + + if (this._extraElementsList) + clipboardString += "\n" + this._extraElementsList.innerText.removeWordBreakCharacters().trim(); + + if (hasStackTrace) { + this._message.stackTrace.callFrames.forEach(function(frame) { + clipboardString += "\n\t" + (frame.functionName || WebInspector.UIString("(anonymous function)")); + if (frame.sourceCodeLocation) + clipboardString += " (" + frame.sourceCodeLocation.originalLocationString() + ")"; + }); + } + + if (!isPrefixOptional || this._enforcesClipboardPrefixString()) + return this._clipboardPrefixString() + clipboardString; + return clipboardString; + } + + // Private + + _appendMessageTextAndArguments(element) + { + if (this._message.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) { + switch (this._message.type) { + case WebInspector.ConsoleMessage.MessageType.Trace: + var args = [WebInspector.UIString("Trace")]; + if (this._message.parameters) { + if (this._message.parameters[0].type === "string") { + var prefixedFormatString = WebInspector.UIString("Trace: %s").format(this._message.parameters[0].description); + args = [prefixedFormatString].concat(this._message.parameters.slice(1)); + } else + args = args.concat(this._message.parameters); + } + this._appendFormattedArguments(element, args); + break; + + case WebInspector.ConsoleMessage.MessageType.Assert: + var args = [WebInspector.UIString("Assertion Failed")]; + if (this._message.parameters) { + if (this._message.parameters[0].type === "string") { + var prefixedFormatString = WebInspector.UIString("Assertion Failed: %s").format(this._message.parameters[0].description); + args = [prefixedFormatString].concat(this._message.parameters.slice(1)); + } else + args = args.concat(this._message.parameters); + } + this._appendFormattedArguments(element, args); + break; + + case WebInspector.ConsoleMessage.MessageType.Dir: + var obj = this._message.parameters ? this._message.parameters[0] : undefined; + this._appendFormattedArguments(element, ["%O", obj]); + break; + + case WebInspector.ConsoleMessage.MessageType.Table: + var args = this._message.parameters; + element.appendChild(this._formatParameterAsTable(args)); + this._extraParameters = null; + break; + + case WebInspector.ConsoleMessage.MessageType.StartGroup: + case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed: + var args = this._message.parameters || [this._message.messageText || WebInspector.UIString("Group")]; + this._formatWithSubstitutionString(args, element); + this._extraParameters = null; + break; + + default: + var args = this._message.parameters || [this._message.messageText]; + this._appendFormattedArguments(element, args); + break; + } + return; + } + + // FIXME: Better handle WebInspector.ConsoleMessage.MessageSource.Network once it has request info. + + var args = this._message.parameters || [this._message.messageText]; + this._appendFormattedArguments(element, args); + } + + _appendSavedResultIndex(element) + { + if (!this._message.savedResultIndex) + return; + + console.assert(this._message instanceof WebInspector.ConsoleCommandResultMessage); + console.assert(this._message.type === WebInspector.ConsoleMessage.MessageType.Result); + + var savedVariableElement = document.createElement("span"); + savedVariableElement.classList.add("console-saved-variable"); + savedVariableElement.textContent = " = $" + this._message.savedResultIndex; + + if (this._objectTree) + this._objectTree.appendTitleSuffix(savedVariableElement); + else + this._messageTextElement.appendChild(savedVariableElement); + } + + _appendLocationLink() + { + if (this._message.source === WebInspector.ConsoleMessage.MessageSource.Network) { + if (this._message.url) { + var anchor = WebInspector.linkifyURLAsNode(this._message.url, this._message.url, "console-message-url"); + anchor.classList.add("console-message-location"); + this._element.appendChild(anchor); + } + return; + } + + var firstNonNativeNonAnonymousCallFrame = this._message.stackTrace.firstNonNativeNonAnonymousCallFrame; + + var callFrame; + if (firstNonNativeNonAnonymousCallFrame) { + // JavaScript errors and console.* methods. + callFrame = firstNonNativeNonAnonymousCallFrame; + } else if (this._message.url && !this._shouldHideURL(this._message.url)) { + // CSS warnings have no stack traces. + callFrame = WebInspector.CallFrame.fromPayload(this._message.target, { + functionName: "", + url: this._message.url, + lineNumber: this._message.line, + columnNumber: this._message.column + }); + } + + if (callFrame) { + const showFunctionName = !!callFrame.functionName; + var locationElement = new WebInspector.CallFrameView(callFrame, showFunctionName); + locationElement.classList.add("console-message-location"); + this._element.appendChild(locationElement); + return; + } + + if (this._message.parameters && this._message.parameters.length === 1) { + var parameter = this._createRemoteObjectIfNeeded(this._message.parameters[0]); + + parameter.findFunctionSourceCodeLocation().then(function(result) { + if (result === WebInspector.RemoteObject.SourceCodeLocationPromise.NoSourceFound || result === WebInspector.RemoteObject.SourceCodeLocationPromise.MissingObjectId) + return; + + var link = this._linkifyLocation(result.sourceCode.url, result.lineNumber, result.columnNumber); + link.classList.add("console-message-location"); + + if (this._element.hasChildNodes()) + this._element.insertBefore(link, this._element.firstChild); + else + this._element.appendChild(link); + }.bind(this)); + } + } + + _appendExtraParameters() + { + if (!this._extraParameters || !this._extraParameters.length) + return; + + this._makeExpandable(); + + // Auto-expand if there are multiple objects. + if (this._extraParameters.length > 1) + this.expand(); + + this._extraElementsList = this._element.appendChild(document.createElement("ol")); + this._extraElementsList.classList.add("console-message-extra-parameters-container"); + + for (var parameter of this._extraParameters) { + var listItemElement = this._extraElementsList.appendChild(document.createElement("li")); + const forceObjectFormat = parameter.type === "object" && (parameter.subtype !== "null" && parameter.subtype !== "regexp" && parameter.subtype !== "node" && parameter.subtype !== "error"); + listItemElement.classList.add("console-message-extra-parameter"); + listItemElement.appendChild(this._formatParameter(parameter, forceObjectFormat)); + } + } + + _appendStackTrace() + { + if (!this._shouldShowStackTrace()) + return; + + this._makeExpandable(); + + // Auto-expand for console.trace. + if (this._message.type === WebInspector.ConsoleMessage.MessageType.Trace) + this.expand(); + + this._stackTraceElement = this._element.appendChild(document.createElement("div")); + this._stackTraceElement.classList.add("console-message-text", "console-message-stack-trace-container"); + + var callFramesElement = new WebInspector.StackTraceView(this._message.stackTrace).element; + this._stackTraceElement.appendChild(callFramesElement); + } + + _createRemoteObjectIfNeeded(parameter) + { + // FIXME: Only pass RemoteObjects here so we can avoid this work. + if (parameter instanceof WebInspector.RemoteObject) + return parameter; + + if (typeof parameter === "object") + return WebInspector.RemoteObject.fromPayload(parameter, this._message.target); + + return WebInspector.RemoteObject.fromPrimitiveValue(parameter); + } + + _appendFormattedArguments(element, parameters) + { + if (!parameters.length) + return; + + for (var i = 0; i < parameters.length; ++i) + parameters[i] = this._createRemoteObjectIfNeeded(parameters[i]); + + var builderElement = element.appendChild(document.createElement("span")); + var shouldFormatWithStringSubstitution = WebInspector.RemoteObject.type(parameters[0]) === "string" && this._message.type !== WebInspector.ConsoleMessage.MessageType.Result; + + // Single object (e.g. console result or logging a non-string object). + if (parameters.length === 1 && !shouldFormatWithStringSubstitution) { + this._extraParameters = null; + builderElement.appendChild(this._formatParameter(parameters[0], false)); + return; + } + + console.assert(this._message.type !== WebInspector.ConsoleMessage.MessageType.Result); + + if (shouldFormatWithStringSubstitution && this._isStackTrace(parameters[0])) + shouldFormatWithStringSubstitution = false; + + // Format string / message / default message. + if (shouldFormatWithStringSubstitution) { + var result = this._formatWithSubstitutionString(parameters, builderElement); + parameters = result.unusedSubstitutions; + this._extraParameters = parameters; + } else { + var defaultMessage = WebInspector.UIString("No message"); + builderElement.append(defaultMessage); + } + + // Trailing parameters. + if (parameters.length) { + let enclosedElement = document.createElement("span"); + + if (parameters.length === 1 && !this._isStackTrace(parameters[0])) { + let parameter = parameters[0]; + + // Single object. Show a preview. + builderElement.append(enclosedElement); + enclosedElement.classList.add("console-message-preview-divider"); + enclosedElement.textContent = " \u2013 "; + + var previewContainer = builderElement.appendChild(document.createElement("span")); + previewContainer.classList.add("console-message-preview"); + + var preview = WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForRemoteObject(parameter, WebInspector.ObjectPreviewView.Mode.Brief); + var isPreviewView = preview instanceof WebInspector.ObjectPreviewView; + + if (isPreviewView) + preview.setOriginatingObjectInfo(parameter, null); + + var previewElement = isPreviewView ? preview.element : preview; + previewContainer.appendChild(previewElement); + + // If this preview is effectively lossless, we can avoid making this console message expandable. + if ((isPreviewView && preview.lossless) || (!isPreviewView && this._shouldConsiderObjectLossless(parameter))) { + this._extraParameters = null; + enclosedElement.classList.add("inline-lossless"); + previewContainer.classList.add("inline-lossless"); + } + } else { + // Multiple objects. Show an indicator. + builderElement.append(" ", enclosedElement); + enclosedElement.classList.add("console-message-enclosed"); + enclosedElement.textContent = "(" + parameters.length + ")"; + } + } + } + + _isStackTrace(parameter) + { + console.assert(parameter instanceof WebInspector.RemoteObject); + + if (WebInspector.RemoteObject.type(parameter) !== "string") + return false; + + return WebInspector.StackTrace.isLikelyStackTrace(parameter.description); + } + + _shouldConsiderObjectLossless(object) + { + if (object.type === "string") { + const description = object.description; + const maxLength = WebInspector.FormattedValue.MAX_PREVIEW_STRING_LENGTH; + const longOrMultiLineString = description.length > maxLength || description.slice(0, maxLength).includes("\n"); + return !longOrMultiLineString; + } + + return object.type !== "object" || object.subtype === "null" || object.subtype === "regexp"; + } + + _formatParameter(parameter, forceObjectFormat) + { + var type; + if (forceObjectFormat) + type = "object"; + else if (parameter instanceof WebInspector.RemoteObject) + type = parameter.subtype || parameter.type; + else { + console.assert(false, "no longer reachable"); + type = typeof parameter; + } + + var formatters = { + "object": this._formatParameterAsObject, + "error": this._formatParameterAsError, + "map": this._formatParameterAsObject, + "set": this._formatParameterAsObject, + "weakmap": this._formatParameterAsObject, + "weakset": this._formatParameterAsObject, + "iterator": this._formatParameterAsObject, + "class": this._formatParameterAsObject, + "proxy": this._formatParameterAsObject, + "array": this._formatParameterAsArray, + "node": this._formatParameterAsNode, + "string": this._formatParameterAsString, + }; + + var formatter = formatters[type] || this._formatParameterAsValue; + + const fragment = document.createDocumentFragment(); + formatter.call(this, parameter, fragment, forceObjectFormat); + return fragment; + } + + _formatParameterAsValue(value, fragment) + { + fragment.appendChild(WebInspector.FormattedValue.createElementForRemoteObject(value)); + } + + _formatParameterAsString(object, fragment) + { + if (this._isStackTrace(object)) { + let stackTrace = WebInspector.StackTrace.fromString(this._message.target, object.description); + if (stackTrace.callFrames.length) { + let stackView = new WebInspector.StackTraceView(stackTrace); + fragment.appendChild(stackView.element); + return; + } + } + + fragment.appendChild(WebInspector.FormattedValue.createLinkifiedElementString(object.description)); + } + + _formatParameterAsNode(object, fragment) + { + fragment.appendChild(WebInspector.FormattedValue.createElementForNode(object)); + } + + _formatParameterAsObject(object, fragment, forceExpansion) + { + // FIXME: Should have a better ObjectTreeView mode for classes (static methods and methods). + this._objectTree = new WebInspector.ObjectTreeView(object, null, this._rootPropertyPathForObject(object), forceExpansion); + fragment.appendChild(this._objectTree.element); + } + + _formatParameterAsError(object, fragment) + { + this._objectTree = new WebInspector.ErrorObjectView(object); + fragment.appendChild(this._objectTree.element); + } + + _formatParameterAsArray(array, fragment) + { + this._objectTree = new WebInspector.ObjectTreeView(array, WebInspector.ObjectTreeView.Mode.Properties, this._rootPropertyPathForObject(array)); + fragment.appendChild(this._objectTree.element); + } + + _rootPropertyPathForObject(object) + { + if (!this._message.savedResultIndex) + return null; + + return new WebInspector.PropertyPath(object, "$" + this._message.savedResultIndex); + } + + _formatWithSubstitutionString(parameters, formattedResult) + { + function parameterFormatter(force, obj) + { + return this._formatParameter(obj, force); + } + + function stringFormatter(obj) + { + return obj.description; + } + + function floatFormatter(obj, token) + { + let value = typeof obj.value === "number" ? obj.value : obj.description; + return String.standardFormatters.f(value, token); + } + + function integerFormatter(obj) + { + let value = typeof obj.value === "number" ? obj.value : obj.description; + return String.standardFormatters.d(value); + } + + var currentStyle = null; + function styleFormatter(obj) + { + currentStyle = {}; + var buffer = document.createElement("span"); + buffer.setAttribute("style", obj.description); + for (var i = 0; i < buffer.style.length; i++) { + var property = buffer.style[i]; + if (isWhitelistedProperty(property)) + currentStyle[property] = buffer.style[property]; + } + } + + function isWhitelistedProperty(property) + { + for (var prefix of ["background", "border", "color", "font", "line", "margin", "padding", "text"]) { + if (property.startsWith(prefix) || property.startsWith("-webkit-" + prefix)) + return true; + } + return false; + } + + // Firebug uses %o for formatting objects. + var formatters = {}; + formatters.o = parameterFormatter.bind(this, false); + formatters.s = stringFormatter; + formatters.f = floatFormatter; + + // Firebug allows both %i and %d for formatting integers. + formatters.i = integerFormatter; + formatters.d = integerFormatter; + + // Firebug uses %c for styling the message. + formatters.c = styleFormatter; + + // Support %O to force object formatting, instead of the type-based %o formatting. + formatters.O = parameterFormatter.bind(this, true); + + function append(a, b) + { + if (b instanceof Node) + a.appendChild(b); + else if (b !== undefined) { + var toAppend = WebInspector.linkifyStringAsFragment(b.toString()); + if (currentStyle) { + var wrapper = document.createElement("span"); + for (var key in currentStyle) + wrapper.style[key] = currentStyle[key]; + wrapper.appendChild(toAppend); + toAppend = wrapper; + } + + a.appendChild(toAppend); + } + return a; + } + + // String.format does treat formattedResult like a Builder, result is an object. + return String.format(parameters[0].description, parameters.slice(1), formatters, formattedResult, append); + } + + _shouldShowStackTrace() + { + if (!this._message.stackTrace.callFrames.length) + return false; + + return this._message.source === WebInspector.ConsoleMessage.MessageSource.Network + || this._message.level === WebInspector.ConsoleMessage.MessageLevel.Error + || this._message.type === WebInspector.ConsoleMessage.MessageType.Trace; + } + + _shouldHideURL(url) + { + return url === "undefined" || url === "[native code]"; + } + + _linkifyLocation(url, lineNumber, columnNumber) + { + return WebInspector.linkifyLocation(url, lineNumber, columnNumber, "console-message-url"); + } + + _userProvidedColumnNames(columnNamesArgument) + { + if (!columnNamesArgument) + return null; + + console.assert(columnNamesArgument instanceof WebInspector.RemoteObject); + + // Single primitive argument. + if (columnNamesArgument.type === "string" || columnNamesArgument.type === "number") + return [String(columnNamesArgument.value)]; + + // Ignore everything that is not an array with property previews. + if (columnNamesArgument.type !== "object" || columnNamesArgument.subtype !== "array" || !columnNamesArgument.preview || !columnNamesArgument.preview.propertyPreviews) + return null; + + // Array. Look into the preview and get string values. + var extractedColumnNames = []; + for (var propertyPreview of columnNamesArgument.preview.propertyPreviews) { + if (propertyPreview.type === "string" || propertyPreview.type === "number") + extractedColumnNames.push(String(propertyPreview.value)); + } + + return extractedColumnNames.length ? extractedColumnNames : null; + } + + _formatParameterAsTable(parameters) + { + var element = document.createElement("span"); + var table = parameters[0]; + if (!table || !table.preview) + return element; + + var rows = []; + var columnNames = []; + var flatValues = []; + var preview = table.preview; + var userProvidedColumnNames = false; + + // User provided columnNames. + var extractedColumnNames = this._userProvidedColumnNames(parameters[1]); + if (extractedColumnNames) { + userProvidedColumnNames = true; + columnNames = extractedColumnNames; + } + + // Check first for valuePreviews in the properties meaning this was an array of objects. + if (preview.propertyPreviews) { + for (var i = 0; i < preview.propertyPreviews.length; ++i) { + var rowProperty = preview.propertyPreviews[i]; + var rowPreview = rowProperty.valuePreview; + if (!rowPreview || !rowPreview.propertyPreviews) + continue; + + var rowValue = {}; + var maxColumnsToRender = 15; + for (var j = 0; j < rowPreview.propertyPreviews.length; ++j) { + var cellProperty = rowPreview.propertyPreviews[j]; + var columnRendered = columnNames.includes(cellProperty.name); + if (!columnRendered) { + if (userProvidedColumnNames || columnNames.length === maxColumnsToRender) + continue; + columnRendered = true; + columnNames.push(cellProperty.name); + } + + rowValue[cellProperty.name] = WebInspector.FormattedValue.createElementForPropertyPreview(cellProperty); + } + rows.push([rowProperty.name, rowValue]); + } + } + + // If there were valuePreviews, convert to a flat list. + if (rows.length) { + columnNames.unshift(WebInspector.UIString("(Index)")); + for (var i = 0; i < rows.length; ++i) { + var rowName = rows[i][0]; + var rowValue = rows[i][1]; + flatValues.push(rowName); + for (var j = 1; j < columnNames.length; ++j) { + var columnName = columnNames[j]; + if (!(columnName in rowValue)) + flatValues.push(emDash); + else + flatValues.push(rowValue[columnName]); + } + } + } + + // If there were no value Previews, then check for an array of values. + if (!flatValues.length && preview.propertyPreviews) { + for (var i = 0; i < preview.propertyPreviews.length; ++i) { + var rowProperty = preview.propertyPreviews[i]; + if (!("value" in rowProperty)) + continue; + + if (!columnNames.length) { + columnNames.push(WebInspector.UIString("Index")); + columnNames.push(WebInspector.UIString("Value")); + } + + flatValues.push(rowProperty.name); + flatValues.push(WebInspector.FormattedValue.createElementForPropertyPreview(rowProperty)); + } + } + + // If no table data show nothing. + if (!flatValues.length) + return element; + + // FIXME: Should we output something extra if the preview is lossless? + + var dataGrid = WebInspector.DataGrid.createSortableDataGrid(columnNames, flatValues); + dataGrid.inline = true; + dataGrid.variableHeightRows = true; + + element.appendChild(dataGrid.element); + + dataGrid.updateLayoutIfNeeded(); + + return element; + } + + _levelString() + { + switch (this._message.level) { + case WebInspector.ConsoleMessage.MessageLevel.Log: + return "Log"; + case WebInspector.ConsoleMessage.MessageLevel.Info: + return "Info"; + case WebInspector.ConsoleMessage.MessageLevel.Warning: + return "Warning"; + case WebInspector.ConsoleMessage.MessageLevel.Debug: + return "Debug"; + case WebInspector.ConsoleMessage.MessageLevel.Error: + return "Error"; + } + } + + _enforcesClipboardPrefixString() + { + return this._message.type !== WebInspector.ConsoleMessage.MessageType.Result; + } + + _clipboardPrefixString() + { + if (this._message.type === WebInspector.ConsoleMessage.MessageType.Result) + return "< "; + + return "[" + this._levelString() + "] "; + } + + _makeExpandable() + { + if (this._expandable) + return; + + this._expandable = true; + + this._element.classList.add("expandable"); + + this._boundClickHandler = this.toggle.bind(this); + this._messageTextElement.addEventListener("click", this._boundClickHandler); + } +}; |