blob: 70269fee33d2d3fc87a45beeba343bb9fa66823b [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
* Copyright (C) 2007, 2008 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 Computer, 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.
*/
/**
* @constructor
* @implements {WebInspector.ViewportElement}
* @param {!WebInspector.ConsoleMessage} consoleMessage
* @param {!WebInspector.Linkifier} linkifier
* @param {number} nestingLevel
*/
WebInspector.ConsoleViewMessage = function(consoleMessage, linkifier, nestingLevel)
{
this._message = consoleMessage;
this._linkifier = linkifier;
this._repeatCount = 1;
this._closeGroupDecorationCount = 0;
this._nestingLevel = nestingLevel;
/** @type {!Array.<!WebInspector.DataGrid>} */
this._dataGrids = [];
/** @type {!Map.<!WebInspector.DataGrid, ?Element>} */
this._dataGridParents = new Map();
/** @type {!Object.<string, function(!WebInspector.RemoteObject, !Element, boolean=)>} */
this._customFormatters = {
"array": this._formatParameterAsArray,
"error": this._formatParameterAsError,
"function": this._formatParameterAsFunction,
"generator": this._formatParameterAsObject,
"iterator": this._formatParameterAsObject,
"map": this._formatParameterAsObject,
"node": this._formatParameterAsNode,
"object": this._formatParameterAsObject,
"set": this._formatParameterAsObject,
"string": this._formatParameterAsString
};
this._previewFormatter = new WebInspector.RemoteObjectPreviewFormatter();
this._searchRegex = null;
}
WebInspector.ConsoleViewMessage.prototype = {
/**
* @return {?WebInspector.Target}
*/
_target: function()
{
return this.consoleMessage().target();
},
/**
* @override
* @return {!Element}
*/
element: function()
{
return this.toMessageElement();
},
/**
* @override
*/
wasShown: function()
{
for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
var dataGrid = this._dataGrids[i];
var parentElement = this._dataGridParents.get(dataGrid) || null;
dataGrid.show(parentElement);
dataGrid.updateWidths();
}
},
/**
* @override
*/
cacheFastHeight: function()
{
this._cachedHeight = this.contentElement().offsetHeight;
},
/**
* @override
*/
willHide: function()
{
for (var i = 0; this._dataGrids && i < this._dataGrids.length; ++i) {
var dataGrid = this._dataGrids[i];
this._dataGridParents.set(dataGrid, dataGrid.element.parentElement);
dataGrid.detach();
}
},
/**
* @return {number}
*/
fastHeight: function()
{
if (this._cachedHeight)
return this._cachedHeight;
const defaultConsoleRowHeight = 18; // Sync with consoleView.css
if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table) {
var table = this._message.parameters[0];
if (table && table.preview)
return defaultConsoleRowHeight * table.preview.properties.length;
}
return defaultConsoleRowHeight;
},
/**
* @return {!WebInspector.ConsoleMessage}
*/
consoleMessage: function()
{
return this._message;
},
_formatMessage: function()
{
this._formattedMessage = createElement("span");
this._formattedMessage.appendChild(WebInspector.Widget.createStyleElement("components/objectValue.css"));
this._formattedMessage.className = "console-message-text source-code";
/**
* @param {string} title
* @return {!Element}
* @this {WebInspector.ConsoleMessage}
*/
function linkifyRequest(title)
{
return WebInspector.Linkifier.linkifyUsingRevealer(/** @type {!WebInspector.NetworkRequest} */ (this.request), title, this.request.url);
}
var consoleMessage = this._message;
if (!this._messageElement) {
if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.ConsoleAPI) {
switch (consoleMessage.type) {
case WebInspector.ConsoleMessage.MessageType.Trace:
this._messageElement = this._format(consoleMessage.parameters || ["console.trace()"]);
break;
case WebInspector.ConsoleMessage.MessageType.Clear:
this._messageElement = createTextNode(WebInspector.UIString("Console was cleared"));
this._formattedMessage.classList.add("console-info");
break;
case WebInspector.ConsoleMessage.MessageType.Assert:
var args = [WebInspector.UIString("Assertion failed:")];
if (consoleMessage.parameters)
args = args.concat(consoleMessage.parameters);
this._messageElement = this._format(args);
break;
case WebInspector.ConsoleMessage.MessageType.Dir:
var obj = consoleMessage.parameters ? consoleMessage.parameters[0] : undefined;
var args = ["%O", obj];
this._messageElement = this._format(args);
break;
case WebInspector.ConsoleMessage.MessageType.Profile:
case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
this._messageElement = this._format([consoleMessage.messageText]);
break;
default:
if (consoleMessage.parameters && consoleMessage.parameters.length === 1 && consoleMessage.parameters[0].type === "string")
this._messageElement = this._tryFormatAsError(/**@type {string} */(consoleMessage.parameters[0].value));
var args = consoleMessage.parameters || [consoleMessage.messageText];
this._messageElement = this._messageElement || this._format(args);
}
} else if (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Network) {
if (consoleMessage.request) {
this._messageElement = createElement("span");
if (consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError) {
this._messageElement.createTextChildren(consoleMessage.request.requestMethod, " ");
this._messageElement.appendChild(WebInspector.Linkifier.linkifyUsingRevealer(consoleMessage.request, consoleMessage.request.url, consoleMessage.request.url));
if (consoleMessage.request.failed)
this._messageElement.createTextChildren(" ", consoleMessage.request.localizedFailDescription);
else
this._messageElement.createTextChildren(" ", String(consoleMessage.request.statusCode), " (", consoleMessage.request.statusText, ")");
} else {
var fragment = WebInspector.linkifyStringAsFragmentWithCustomLinkifier(consoleMessage.messageText, linkifyRequest.bind(consoleMessage));
this._messageElement.appendChild(fragment);
}
} else {
var url = consoleMessage.url;
if (url) {
var isExternal = !WebInspector.resourceForURL(url) && !WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(url);
this._anchorElement = WebInspector.linkifyURLAsNode(url, url, "console-message-url", isExternal);
}
this._messageElement = this._format([consoleMessage.messageText]);
}
} else {
var args = consoleMessage.parameters || [consoleMessage.messageText];
this._messageElement = this._format(args);
}
}
if (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.request) {
if (consoleMessage.scriptId) {
this._anchorElement = this._linkifyScriptId(consoleMessage.scriptId, consoleMessage.url || "", consoleMessage.line, consoleMessage.column);
} else {
var showBlackboxed = (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.ConsoleAPI);
var debuggerModel = WebInspector.DebuggerModel.fromTarget(this._target());
var callFrame = WebInspector.DebuggerPresentationUtils.callFrameAnchorFromStackTrace(debuggerModel, consoleMessage.stackTrace, consoleMessage.asyncStackTrace, showBlackboxed);
if (callFrame && callFrame.scriptId)
this._anchorElement = this._linkifyCallFrame(callFrame);
else if (consoleMessage.url && consoleMessage.url !== "undefined")
this._anchorElement = this._linkifyLocation(consoleMessage.url, consoleMessage.line, consoleMessage.column);
}
}
this._formattedMessage.appendChild(this._messageElement);
if (this._anchorElement) {
// Append a space to prevent the anchor text from being glued to the console message when the user selects and copies the console messages.
this._anchorElement.appendChild(createTextNode(" "));
this._formattedMessage.insertBefore(this._anchorElement, this._formattedMessage.firstChild);
}
var dumpStackTrace = (!!consoleMessage.stackTrace || !!consoleMessage.asyncStackTrace) && (consoleMessage.source === WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError || consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace);
if (dumpStackTrace) {
var treeOutline = new TreeOutline();
treeOutline.element.classList.add("outline-disclosure", "outline-disclosure-no-padding");
var content = this._formattedMessage;
var root = new TreeElement(content);
root.toggleOnClick = true;
root.selectable = false;
content.treeElementForTest = root;
treeOutline.appendChild(root);
if (consoleMessage.type === WebInspector.ConsoleMessage.MessageType.Trace)
root.expand();
this._populateStackTraceTreeElement(root);
this._formattedMessage = treeOutline.element;
}
},
/**
* @return {!Element}
*/
formattedMessage: function()
{
if (!this._formattedMessage)
this._formatMessage();
return this._formattedMessage;
},
/**
* @param {string} url
* @param {number} lineNumber
* @param {number} columnNumber
* @return {?Element}
*/
_linkifyLocation: function(url, lineNumber, columnNumber)
{
var target = this._target();
if (!target)
return null;
// FIXME(62725): stack trace line/column numbers are one-based.
lineNumber = lineNumber ? lineNumber - 1 : 0;
columnNumber = columnNumber ? columnNumber - 1 : 0;
return this._linkifier.linkifyScriptLocation(target, null, url, lineNumber, columnNumber, "console-message-url");
},
/**
* @param {!ConsoleAgent.CallFrame} callFrame
* @return {?Element}
*/
_linkifyCallFrame: function(callFrame)
{
var target = this._target();
return this._linkifier.linkifyConsoleCallFrame(target, callFrame, "console-message-url");
},
/**
* @param {string} scriptId
* @param {string} url
* @param {number} lineNumber
* @param {number} columnNumber
* @return {?Element}
*/
_linkifyScriptId: function(scriptId, url, lineNumber, columnNumber)
{
var target = this._target();
if (!target)
return null;
// FIXME(62725): stack trace line/column numbers are one-based.
lineNumber = lineNumber ? lineNumber - 1 : 0;
columnNumber = columnNumber ? columnNumber - 1 : 0;
return this._linkifier.linkifyScriptLocation(target, scriptId, url, lineNumber, columnNumber, "console-message-url");
},
_format: function(parameters)
{
// This node is used like a Builder. Values are continually appended onto it.
var formattedResult = createElement("span");
if (!parameters.length)
return formattedResult;
var target = this._target();
// Formatting code below assumes that parameters are all wrappers whereas frontend console
// API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
for (var i = 0; i < parameters.length; ++i) {
// FIXME: Only pass runtime wrappers here.
if (parameters[i] instanceof WebInspector.RemoteObject)
continue;
if (!target) {
parameters[i] = WebInspector.RemoteObject.fromLocalObject(parameters[i]);
continue;
}
if (typeof parameters[i] === "object")
parameters[i] = target.runtimeModel.createRemoteObject(parameters[i]);
else
parameters[i] = target.runtimeModel.createRemoteObjectFromPrimitiveValue(parameters[i]);
}
// There can be string log and string eval result. We distinguish between them based on message type.
var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && (this._message.type !== WebInspector.ConsoleMessage.MessageType.Result || this._message.level === WebInspector.ConsoleMessage.MessageLevel.Error || this._message.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError);
// Multiple parameters with the first being a format string. Save unused substitutions.
if (shouldFormatMessage) {
// Multiple parameters with the first being a format string. Save unused substitutions.
var result = this._formatWithSubstitutionString(parameters[0].description, parameters.slice(1), formattedResult);
parameters = result.unusedSubstitutions;
if (parameters.length)
formattedResult.createTextChild(" ");
}
if (this._message.type === WebInspector.ConsoleMessage.MessageType.Table) {
formattedResult.appendChild(this._formatParameterAsTable(parameters));
return formattedResult;
}
// Single parameter, or unused substitutions from above.
for (var i = 0; i < parameters.length; ++i) {
// Inline strings when formatting.
if (shouldFormatMessage && parameters[i].type === "string")
formattedResult.appendChild(WebInspector.linkifyStringAsFragment(parameters[i].description));
else
formattedResult.appendChild(this._formatParameter(parameters[i], false, true));
if (i < parameters.length - 1)
formattedResult.createTextChild(" ");
}
return formattedResult;
},
/**
* @param {!WebInspector.RemoteObject} output
* @param {boolean=} forceObjectFormat
* @param {boolean=} includePreview
* @return {!Element}
*/
_formatParameter: function(output, forceObjectFormat, includePreview)
{
if (output.customPreview()) {
return (new WebInspector.CustomPreviewComponent(output)).element;
}
var type = forceObjectFormat ? "object" : (output.subtype || output.type);
var formatter = this._customFormatters[type] || this._formatParameterAsValue;
var span = createElement("span");
span.className = "object-value-" + type + " source-code";
formatter.call(this, output, span, includePreview);
return span;
},
/**
* @param {!WebInspector.RemoteObject} obj
* @param {!Element} elem
*/
_formatParameterAsValue: function(obj, elem)
{
elem.createTextChild(obj.description || "");
if (obj.objectId)
elem.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, obj), false);
},
/**
* @param {!WebInspector.RemoteObject} obj
* @param {!Element} elem
* @param {boolean=} includePreview
*/
_formatParameterAsObject: function(obj, elem, includePreview)
{
this._formatParameterAsArrayOrObject(obj, elem, includePreview);
},
/**
* @param {!WebInspector.RemoteObject} obj
* @param {!Element} elem
* @param {boolean=} includePreview
*/
_formatParameterAsArrayOrObject: function(obj, elem, includePreview)
{
var titleElement = createElement("span");
if (includePreview && obj.preview) {
titleElement.classList.add("console-object-preview");
var lossless = this._previewFormatter.appendObjectPreview(titleElement, obj.preview);
if (lossless) {
elem.appendChild(titleElement);
titleElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, obj), false);
return;
}
} else {
if (obj.type === "function") {
WebInspector.ObjectPropertiesSection.formatObjectAsFunction(obj, titleElement, false);
titleElement.classList.add("object-value-function");
} else {
titleElement.createTextChild(obj.description || "");
}
}
var note = titleElement.createChild("span", "object-info-state-note");
note.title = WebInspector.UIString("Object value at left was snapshotted when logged, value below was evaluated just now.");
var section = new WebInspector.ObjectPropertiesSection(obj, titleElement);
section.enableContextMenu();
elem.appendChild(section.element);
section.element.classList.add("console-view-object-properties-section");
},
/**
* @param {!WebInspector.RemoteObject} func
* @param {!Element} element
* @param {boolean=} includePreview
*/
_formatParameterAsFunction: function(func, element, includePreview)
{
WebInspector.ObjectPropertiesSection.formatObjectAsFunction(func, element, true, includePreview);
element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, func), false);
},
/**
* @param {!WebInspector.RemoteObject} obj
* @param {!Event} event
*/
_contextMenuEventFired: function(obj, event)
{
var contextMenu = new WebInspector.ContextMenu(event);
contextMenu.appendApplicableItems(obj);
contextMenu.show();
},
/**
* @param {?WebInspector.RemoteObject} object
* @param {!Array.<!RuntimeAgent.PropertyPreview>} propertyPath
* @return {!Element}
*/
_renderPropertyPreviewOrAccessor: function(object, propertyPath)
{
var property = propertyPath.peekLast();
if (property.type === "accessor")
return this._formatAsAccessorProperty(object, propertyPath.select("name"), false);
return this._previewFormatter.renderPropertyPreview(property.type, /** @type {string} */ (property.subtype), property.value);
},
/**
* @param {!WebInspector.RemoteObject} object
* @param {!Element} elem
*/
_formatParameterAsNode: function(object, elem)
{
WebInspector.Renderer.renderPromise(object).then(appendRenderer, failedToRender.bind(this));
/**
* @param {!Element} rendererElement
*/
function appendRenderer(rendererElement)
{
elem.appendChild(rendererElement);
}
/**
* @this {WebInspector.ConsoleViewMessage}
*/
function failedToRender()
{
this._formatParameterAsObject(object, elem, false);
}
},
/**
* @param {!WebInspector.RemoteObject} array
* @return {boolean}
*/
useArrayPreviewInFormatter: function(array)
{
return this._message.type !== WebInspector.ConsoleMessage.MessageType.DirXML;
},
/**
* @param {!WebInspector.RemoteObject} array
* @param {!Element} elem
*/
_formatParameterAsArray: function(array, elem)
{
var maxFlatArrayLength = 100;
if (this.useArrayPreviewInFormatter(array) || array.arrayLength() > maxFlatArrayLength)
this._formatParameterAsArrayOrObject(array, elem, this.useArrayPreviewInFormatter(array) || array.arrayLength() <= maxFlatArrayLength);
else
array.getAllProperties(false, this._printArray.bind(this, array, elem));
},
/**
* @param {!Array.<!WebInspector.RemoteObject>} parameters
* @return {!Element}
*/
_formatParameterAsTable: function(parameters)
{
var element = createElementWithClass("div", "console-message-formatted-table");
var table = parameters[0];
if (!table || !table.preview)
return element;
var columnNames = [];
var preview = table.preview;
var rows = [];
for (var i = 0; i < preview.properties.length; ++i) {
var rowProperty = preview.properties[i];
var rowPreview = rowProperty.valuePreview;
if (!rowPreview)
continue;
var rowValue = {};
const maxColumnsToRender = 20;
for (var j = 0; j < rowPreview.properties.length; ++j) {
var cellProperty = rowPreview.properties[j];
var columnRendered = columnNames.indexOf(cellProperty.name) != -1;
if (!columnRendered) {
if (columnNames.length === maxColumnsToRender)
continue;
columnRendered = true;
columnNames.push(cellProperty.name);
}
if (columnRendered) {
var cellElement = this._renderPropertyPreviewOrAccessor(table, [rowProperty, cellProperty]);
cellElement.classList.add("console-message-nowrap-below");
rowValue[cellProperty.name] = cellElement;
}
}
rows.push([rowProperty.name, rowValue]);
}
var flatValues = [];
for (var i = 0; i < rows.length; ++i) {
var rowName = rows[i][0];
var rowValue = rows[i][1];
flatValues.push(rowName);
for (var j = 0; j < columnNames.length; ++j)
flatValues.push(rowValue[columnNames[j]]);
}
var dataGridContainer = element.createChild("span");
if (!preview.lossless || !flatValues.length) {
element.appendChild(this._formatParameter(table, true, false));
if (!flatValues.length)
return element;
}
columnNames.unshift(WebInspector.UIString("(index)"));
var dataGrid = WebInspector.SortableDataGrid.create(columnNames, flatValues);
dataGrid.renderInline();
this._dataGrids.push(dataGrid);
this._dataGridParents.set(dataGrid, dataGridContainer);
return element;
},
/**
* @param {!WebInspector.RemoteObject} output
* @param {!Element} elem
*/
_formatParameterAsString: function(output, elem)
{
var span = createElement("span");
span.className = "object-value-string source-code";
span.appendChild(WebInspector.linkifyStringAsFragment(output.description || ""));
// Make black quotes.
elem.classList.remove("object-value-string");
elem.createTextChild("\"");
elem.appendChild(span);
elem.createTextChild("\"");
},
/**
* @param {!WebInspector.RemoteObject} output
* @param {!Element} elem
*/
_formatParameterAsError: function(output, elem)
{
var span = elem.createChild("span", "object-value-error source-code");
var text = output.description || "";
var lines = text.split("\n", 2);
span.appendChild(WebInspector.linkifyStringAsFragment(lines[0]));
if (lines.length > 1) {
var detailedLink = elem.createChild("a");
detailedLink.textContent = "(\u2026)";
function showDetailed(event)
{
span.removeChildren();
detailedLink.remove();
span.appendChild(WebInspector.linkifyStringAsFragment(text));
event.consume(true);
}
detailedLink._showDetailedForTest = showDetailed.bind(null, new MouseEvent('click'));
detailedLink.addEventListener("click", showDetailed, false);
}
},
/**
* @param {!WebInspector.RemoteObject} array
* @param {!Element} elem
* @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
*/
_printArray: function(array, elem, properties)
{
if (!properties) {
this._formatParameterAsObject(array, elem, false);
return;
}
var elements = [];
for (var i = 0; i < properties.length; ++i) {
var property = properties[i];
var name = property.name;
if (isNaN(name))
continue;
if (property.getter)
elements[name] = this._formatAsAccessorProperty(array, [name], true);
else if (property.value)
elements[name] = this._formatAsArrayEntry(property.value);
}
elem.createTextChild("[");
var lastNonEmptyIndex = -1;
function appendUndefined(elem, index)
{
if (index - lastNonEmptyIndex <= 1)
return;
var span = elem.createChild("span", "object-value-undefined");
span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1);
}
var length = array.arrayLength();
for (var i = 0; i < length; ++i) {
var element = elements[i];
if (!element)
continue;
if (i - lastNonEmptyIndex > 1) {
appendUndefined(elem, i);
elem.createTextChild(", ");
}
elem.appendChild(element);
lastNonEmptyIndex = i;
if (i < length - 1)
elem.createTextChild(", ");
}
appendUndefined(elem, length);
elem.createTextChild("]");
elem.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, array), false);
},
/**
* @param {!WebInspector.RemoteObject} output
* @return {!Element}
*/
_formatAsArrayEntry: function(output)
{
// Prevent infinite expansion of cross-referencing arrays.
return this._formatParameter(output, output.subtype === "array", false);
},
/**
* @param {?WebInspector.RemoteObject} object
* @param {!Array.<string>} propertyPath
* @param {boolean} isArrayEntry
* @return {!Element}
*/
_formatAsAccessorProperty: function(object, propertyPath, isArrayEntry)
{
var rootElement = WebInspector.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan(object, propertyPath, onInvokeGetterClick.bind(this));
/**
* @param {?WebInspector.RemoteObject} result
* @param {boolean=} wasThrown
* @this {WebInspector.ConsoleViewMessage}
*/
function onInvokeGetterClick(result, wasThrown)
{
if (!result)
return;
rootElement.removeChildren();
if (wasThrown) {
var element = rootElement.createChild("span", "error-message");
element.textContent = WebInspector.UIString("<exception>");
element.title = /** @type {string} */ (result.description);
} else if (isArrayEntry) {
rootElement.appendChild(this._formatAsArrayEntry(result));
} else {
// Make a PropertyPreview from the RemoteObject similar to the backend logic.
const maxLength = 100;
var type = result.type;
var subtype = result.subtype;
var description = "";
if (type !== "function" && result.description) {
if (type === "string" || subtype === "regexp")
description = result.description.trimMiddle(maxLength);
else
description = result.description.trimEnd(maxLength);
}
rootElement.appendChild(this._previewFormatter.renderPropertyPreview(type, subtype, description));
}
}
return rootElement;
},
/**
* @param {string} format
* @param {!Array.<string>} parameters
* @param {!Element} formattedResult
*/
_formatWithSubstitutionString: function(format, parameters, formattedResult)
{
var formatters = {};
/**
* @param {boolean} force
* @param {!WebInspector.RemoteObject} obj
* @return {!Element}
* @this {WebInspector.ConsoleViewMessage}
*/
function parameterFormatter(force, obj)
{
return this._formatParameter(obj, force, false);
}
function stringFormatter(obj)
{
return obj.description;
}
function floatFormatter(obj)
{
if (typeof obj.value !== "number")
return "NaN";
return obj.value;
}
function integerFormatter(obj)
{
if (typeof obj.value !== "number")
return "NaN";
return Math.floor(obj.value);
}
function bypassFormatter(obj)
{
return (obj instanceof Node) ? obj : "";
}
var currentStyle = null;
function styleFormatter(obj)
{
currentStyle = {};
var buffer = createElement("span");
buffer.setAttribute("style", obj.description);
for (var i = 0; i < buffer.style.length; i++) {
var property = buffer.style[i];
var value = buffer.style.getPropertyValue(property);
if (!value.startsWith("url(") && isWhitelistedProperty(property))
currentStyle[property] = buffer.style[property];
}
}
function isWhitelistedProperty(property)
{
var prefixes = ["background", "border", "color", "font", "line", "margin", "padding", "text", "-webkit-background", "-webkit-border", "-webkit-font", "-webkit-margin", "-webkit-padding", "-webkit-text"];
for (var i = 0; i < prefixes.length; i++) {
if (property.startsWith(prefixes[i]))
return true;
}
return false;
}
// Firebug uses %o for formatting objects.
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);
formatters._ = bypassFormatter;
function append(a, b)
{
if (b instanceof Node)
a.appendChild(b);
else if (typeof b !== "undefined") {
var toAppend = WebInspector.linkifyStringAsFragment(String(b));
if (currentStyle) {
var wrapper = createElement('span');
wrapper.appendChild(toAppend);
applyCurrentStyle(wrapper);
for (var i = 0; i < wrapper.children.length; ++i)
applyCurrentStyle(wrapper.children[i]);
toAppend = wrapper;
}
a.appendChild(toAppend);
}
return a;
}
/**
* @param {!Element} element
*/
function applyCurrentStyle(element)
{
for (var key in currentStyle)
element.style[key] = currentStyle[key];
}
// String.format does treat formattedResult like a Builder, result is an object.
return String.format(format, parameters, formatters, formattedResult, append);
},
/**
* @return {boolean}
*/
matchesFilterRegex: function(regexObject)
{
regexObject.lastIndex = 0;
var text = this.searchableElement().deepTextContent();
if (this._anchorElement)
text += " " + this._anchorElement.textContent;
return regexObject.test(text);
},
/**
* @param {boolean} show
*/
updateTimestamp: function(show)
{
if (!this._formattedMessage)
return;
if (show && !this.timestampElement) {
this.timestampElement = createElementWithClass("span", "console-timestamp");
this.timestampElement.textContent = (new Date(this._message.timestamp)).toConsoleTime() + " ";
var afterRepeatCountChild = this._repeatCountElement && this._repeatCountElement.nextSibling;
this._formattedMessage.insertBefore(this.timestampElement, this._formattedMessage.firstChild);
return;
}
if (!show && this.timestampElement) {
this.timestampElement.remove();
delete this.timestampElement;
}
},
/**
* @return {number}
*/
nestingLevel: function()
{
return this._nestingLevel;
},
resetCloseGroupDecorationCount: function()
{
if (!this._closeGroupDecorationCount)
return;
this._closeGroupDecorationCount = 0;
this._updateCloseGroupDecorations();
},
incrementCloseGroupDecorationCount: function()
{
++this._closeGroupDecorationCount;
this._updateCloseGroupDecorations();
},
_updateCloseGroupDecorations: function()
{
if (!this._nestingLevelMarkers)
return;
for (var i = 0, n = this._nestingLevelMarkers.length; i < n; ++i) {
var marker = this._nestingLevelMarkers[i];
marker.classList.toggle("group-closed", n - i <= this._closeGroupDecorationCount);
}
},
/**
* @return {!Element}
*/
contentElement: function()
{
if (this._element)
return this._element;
var element = createElementWithClass("div", "console-message");
this._element = element;
if (this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this._message.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
element.classList.add("console-group-title");
element.appendChild(this.formattedMessage());
if (this._repeatCount > 1)
this._showRepeatCountElement();
this.updateTimestamp(WebInspector.moduleSetting("consoleTimestampsEnabled").get());
return this._element;
},
/**
* @return {!Element}
*/
toMessageElement: function()
{
if (this._wrapperElement)
return this._wrapperElement;
this._wrapperElement = createElement("div");
this.updateMessageElement();
return this._wrapperElement;
},
updateMessageElement: function()
{
if (!this._wrapperElement)
return;
this._wrapperElement.className = "console-message-wrapper";
this._wrapperElement.removeChildren();
this._nestingLevelMarkers = [];
for (var i = 0; i < this._nestingLevel; ++i)
this._nestingLevelMarkers.push(this._wrapperElement.createChild("div", "nesting-level-marker"));
this._updateCloseGroupDecorations();
this._wrapperElement.message = this;
switch (this._message.level) {
case WebInspector.ConsoleMessage.MessageLevel.Log:
this._wrapperElement.classList.add("console-log-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Debug:
this._wrapperElement.classList.add("console-debug-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
this._wrapperElement.classList.add("console-warning-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
this._wrapperElement.classList.add("console-error-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.RevokedError:
this._wrapperElement.classList.add("console-revokedError-level");
break;
case WebInspector.ConsoleMessage.MessageLevel.Info:
this._wrapperElement.classList.add("console-info-level");
break;
}
this._wrapperElement.appendChild(this.contentElement());
},
/**
* @param {!TreeElement} parentTreeElement
*/
_populateStackTraceTreeElement: function(parentTreeElement)
{
var target = this._target();
if (!target)
return;
var content = WebInspector.DOMPresentationUtils.buildStackTracePreviewContents(target,
this._linkifier, this._message.stackTrace, this._message.asyncStackTrace);
var treeElement = new TreeElement(content);
treeElement.selectable = false;
parentTreeElement.appendChild(treeElement);
},
resetIncrementRepeatCount: function()
{
this._repeatCount = 1;
if (!this._repeatCountElement)
return;
this._repeatCountElement.remove();
delete this._repeatCountElement;
},
incrementRepeatCount: function()
{
this._repeatCount++;
this._showRepeatCountElement();
},
_showRepeatCountElement: function()
{
if (!this._element)
return;
if (!this._repeatCountElement) {
this._repeatCountElement = createElement("span");
this._repeatCountElement.className = "bubble-repeat-count";
this._element.insertBefore(this._repeatCountElement, this._element.firstChild);
this._element.classList.add("repeated-message");
}
this._repeatCountElement.textContent = this._repeatCount;
},
/**
* @override
* @return {string}
*/
toString: function()
{
var sourceString;
switch (this._message.source) {
case WebInspector.ConsoleMessage.MessageSource.XML:
sourceString = "XML";
break;
case WebInspector.ConsoleMessage.MessageSource.JS:
sourceString = "JavaScript";
break;
case WebInspector.ConsoleMessage.MessageSource.Network:
sourceString = "Network";
break;
case WebInspector.ConsoleMessage.MessageSource.ConsoleAPI:
sourceString = "ConsoleAPI";
break;
case WebInspector.ConsoleMessage.MessageSource.Storage:
sourceString = "Storage";
break;
case WebInspector.ConsoleMessage.MessageSource.AppCache:
sourceString = "AppCache";
break;
case WebInspector.ConsoleMessage.MessageSource.Rendering:
sourceString = "Rendering";
break;
case WebInspector.ConsoleMessage.MessageSource.CSS:
sourceString = "CSS";
break;
case WebInspector.ConsoleMessage.MessageSource.Security:
sourceString = "Security";
break;
case WebInspector.ConsoleMessage.MessageSource.Other:
sourceString = "Other";
break;
}
var typeString;
switch (this._message.type) {
case WebInspector.ConsoleMessage.MessageType.Log:
typeString = "Log";
break;
case WebInspector.ConsoleMessage.MessageType.Dir:
typeString = "Dir";
break;
case WebInspector.ConsoleMessage.MessageType.DirXML:
typeString = "Dir XML";
break;
case WebInspector.ConsoleMessage.MessageType.Trace:
typeString = "Trace";
break;
case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
case WebInspector.ConsoleMessage.MessageType.StartGroup:
typeString = "Start Group";
break;
case WebInspector.ConsoleMessage.MessageType.EndGroup:
typeString = "End Group";
break;
case WebInspector.ConsoleMessage.MessageType.Assert:
typeString = "Assert";
break;
case WebInspector.ConsoleMessage.MessageType.Result:
typeString = "Result";
break;
case WebInspector.ConsoleMessage.MessageType.Profile:
case WebInspector.ConsoleMessage.MessageType.ProfileEnd:
typeString = "Profiling";
break;
}
var levelString;
switch (this._message.level) {
case WebInspector.ConsoleMessage.MessageLevel.Log:
levelString = "Log";
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
levelString = "Warning";
break;
case WebInspector.ConsoleMessage.MessageLevel.Debug:
levelString = "Debug";
break;
case WebInspector.ConsoleMessage.MessageLevel.Error:
levelString = "Error";
break;
case WebInspector.ConsoleMessage.MessageLevel.RevokedError:
levelString = "RevokedError";
break;
case WebInspector.ConsoleMessage.MessageLevel.Info:
levelString = "Info";
break;
}
return sourceString + " " + typeString + " " + levelString + ": " + this.formattedMessage().textContent + "\n" + this._message.url + " line " + this._message.line;
},
get text()
{
return this._message.messageText;
},
/**
* @param {?RegExp} regex
*/
setSearchRegex: function(regex)
{
if (this._searchHiglightNodeChanges && this._searchHiglightNodeChanges.length)
WebInspector.revertDomChanges(this._searchHiglightNodeChanges);
this._searchRegex = regex;
this._searchHighlightNodes = [];
this._searchHiglightNodeChanges = [];
if (!this._searchRegex)
return;
var text = this.searchableElement().deepTextContent();
var match;
this._searchRegex.lastIndex = 0;
var sourceRanges = [];
while ((match = this._searchRegex.exec(text)) && match[0])
sourceRanges.push(new WebInspector.SourceRange(match.index, match[0].length));
if (sourceRanges.length && this.searchableElement())
this._searchHighlightNodes = WebInspector.highlightSearchResults(this.searchableElement(), sourceRanges, this._searchHiglightNodeChanges);
},
/**
* @return {?RegExp}
*/
searchRegex: function()
{
return this._searchRegex;
},
/**
* @return {number}
*/
searchCount: function()
{
return this._searchHighlightNodes.length;
},
/**
* @return {!Element}
*/
searchHighlightNode: function(index)
{
return this._searchHighlightNodes[index];
},
/**
* @return {!Element}
*/
searchableElement: function()
{
this.formattedMessage();
return this._messageElement;
},
/**
* @param {string} string
* @return {?Element}
*/
_tryFormatAsError: function(string)
{
/**
* @param {string} prefix
*/
function startsWith(prefix)
{
return string.startsWith(prefix);
}
var errorPrefixes = ["EvalError", "ReferenceError", "SyntaxError", "TypeError", "RangeError", "Error", "URIError"];
var target = this._target();
if (!target || !errorPrefixes.some(startsWith))
return null;
var debuggerModel = WebInspector.DebuggerModel.fromTarget(target);
if (!debuggerModel)
return null;
var lines = string.split("\n");
var links = [];
var position = 0;
for (var i = 0; i < lines.length; ++i) {
position += i > 0 ? lines[i - 1].length + 1 : 0;
var isCallFrameLine = /^\s*at\s/.test(lines[i]);
if (!isCallFrameLine && links.length)
return null;
if (!isCallFrameLine)
continue;
var openBracketIndex = lines[i].indexOf("(");
var closeBracketIndex = lines[i].indexOf(")");
var hasOpenBracket = openBracketIndex !== -1;
var hasCloseBracket = closeBracketIndex !== -1;
if ((openBracketIndex > closeBracketIndex) || (hasOpenBracket ^ hasCloseBracket))
return null;
var left = hasOpenBracket ? openBracketIndex + 1 : lines[i].indexOf("at") + 3;
var right = hasOpenBracket ? closeBracketIndex : lines[i].length;
var linkCandidate = lines[i].substring(left, right);
var splitResult = WebInspector.ParsedURL.splitLineAndColumn(linkCandidate);
if (!splitResult)
return null;
var parsed = splitResult.url.asParsedURL();
var url;
if (parsed)
url = parsed.url;
else if (debuggerModel.scriptsForSourceURL(splitResult.url).length)
url = splitResult.url;
else if (splitResult.url === "<anonymous>")
continue;
else
return null;
links.push({url: url, positionLeft: position + left, positionRight: position + right, lineNumber: splitResult.lineNumber, columnNumber: splitResult.columnNumber});
}
if (!links.length)
return null;
var formattedResult = createElement("span");
var start = 0;
for (var i = 0; i < links.length; ++i) {
formattedResult.appendChild(WebInspector.linkifyStringAsFragment(string.substring(start, links[i].positionLeft)));
formattedResult.appendChild(this._linkifier.linkifyScriptLocation(target, null, links[i].url, links[i].lineNumber, links[i].columnNumber));
start = links[i].positionRight;
}
if (start != string.length)
formattedResult.appendChild(WebInspector.linkifyStringAsFragment(string.substring(start)));
return formattedResult;
}
}
/**
* @constructor
* @extends {WebInspector.ConsoleViewMessage}
* @param {!WebInspector.ConsoleMessage} consoleMessage
* @param {!WebInspector.Linkifier} linkifier
* @param {number} nestingLevel
*/
WebInspector.ConsoleGroupViewMessage = function(consoleMessage, linkifier, nestingLevel)
{
console.assert(consoleMessage.isGroupStartMessage());
WebInspector.ConsoleViewMessage.call(this, consoleMessage, linkifier, nestingLevel);
this.setCollapsed(consoleMessage.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed);
}
WebInspector.ConsoleGroupViewMessage.prototype = {
/**
* @param {boolean} collapsed
*/
setCollapsed: function(collapsed)
{
this._collapsed = collapsed;
if (this._wrapperElement)
this._wrapperElement.classList.toggle("collapsed", this._collapsed);
},
/**
* @return {boolean}
*/
collapsed: function()
{
return this._collapsed;
},
/**
* @override
* @return {!Element}
*/
toMessageElement: function()
{
if (!this._wrapperElement) {
WebInspector.ConsoleViewMessage.prototype.toMessageElement.call(this);
this._wrapperElement.classList.toggle("collapsed", this._collapsed);
}
return this._wrapperElement;
},
__proto__: WebInspector.ConsoleViewMessage.prototype
}