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.
34 WebInspector.ConsoleStackFrame = function()
37 this.functionName = undefined;
38 this.lineNumber = undefined;
39 this.columnNumber = undefined;
44 * @param {Array.<WebInspector.ConsoleStackFrame>=} stackTrace
45 * @param {number=} request
47 WebInspector.ConsoleMessage = function(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request)
54 this.repeatCount = repeatCount;
55 this.repeatDelta = repeatCount;
56 this.totalRepeatCount = repeatCount;
57 this._messageText = message;
58 this._parameters = parameters;
59 this._stackTrace = stackTrace;
60 this._request = request;
62 if (stackTrace && stackTrace.length) {
63 var topCallFrame = stackTrace[0];
65 this.url = topCallFrame.url;
67 this.line = topCallFrame.lineNumber;
70 this._formatMessage();
73 WebInspector.ConsoleMessage.createTextMessage = function(text, level)
75 level = level || WebInspector.ConsoleMessage.MessageLevel.Log;
76 return new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.JS, WebInspector.ConsoleMessage.MessageType.Log, level, 0, null, 1, null, [text], null);
79 WebInspector.ConsoleMessage.prototype = {
80 _formatMessage: function()
82 var stackTrace = this._stackTrace;
84 this._formattedMessage = document.createElement("span");
85 this._formattedMessage.className = "console-message-text source-code";
89 case WebInspector.ConsoleMessage.MessageType.Trace:
90 messageText = document.createTextNode("console.trace()");
92 case WebInspector.ConsoleMessage.MessageType.UncaughtException:
93 messageText = document.createTextNode(this._messageText);
95 case WebInspector.ConsoleMessage.MessageType.NetworkError:
97 stackTrace = this._request.stackTrace;
99 messageText = document.createElement("span");
100 messageText.appendChild(document.createTextNode(this._request.requestMethod + " "));
101 var anchor = WebInspector.linkifyURLAsNode(this._request.url);
102 anchor.setAttribute("request_id", this._request.requestId);
103 anchor.setAttribute("preferred_panel", "network");
104 messageText.appendChild(anchor);
105 if (this._request.failed)
106 messageText.appendChild(document.createTextNode(" " + this._request.localizedFailDescription));
108 messageText.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")"));
110 var isExternal = !WebInspector.resourceForURL(this.url);
111 var anchor = WebInspector.linkifyURLAsNode(this.url, this.url, "console-message-url", isExternal);
112 this._formattedMessage.appendChild(anchor);
113 messageText = this._format([this._messageText]);
116 case WebInspector.ConsoleMessage.MessageType.Assert:
117 var args = [WebInspector.UIString("Assertion failed:")];
118 if (this._parameters)
119 args = args.concat(this._parameters);
120 messageText = this._format(args);
122 case WebInspector.ConsoleMessage.MessageType.Object:
123 var obj = this._parameters ? this._parameters[0] : undefined;
124 var args = ["%O", obj];
125 messageText = this._format(args);
128 var args = this._parameters || [this._messageText];
129 messageText = this._format(args);
133 if (this.type !== WebInspector.ConsoleMessage.MessageType.NetworkError) {
134 if (this._stackTrace && this._stackTrace.length && this._stackTrace[0].url) {
135 var urlElement = this._linkifyCallFrame(this._stackTrace[0]);
136 this._formattedMessage.appendChild(urlElement);
137 } else if (this.url && this.url !== "undefined") {
138 var urlElement = this._linkifyLocation(this.url, this.line, 0);
139 this._formattedMessage.appendChild(urlElement);
143 this._formattedMessage.appendChild(messageText);
145 if (this._stackTrace) {
147 case WebInspector.ConsoleMessage.MessageType.Trace:
148 case WebInspector.ConsoleMessage.MessageType.UncaughtException:
149 case WebInspector.ConsoleMessage.MessageType.NetworkError:
150 case WebInspector.ConsoleMessage.MessageType.Assert: {
151 var ol = document.createElement("ol");
152 ol.className = "outline-disclosure";
153 var treeOutline = new TreeOutline(ol);
155 var content = this._formattedMessage;
156 var root = new TreeElement(content, null, true);
157 content.treeElementForTest = root;
158 treeOutline.appendChild(root);
159 if (this.type === WebInspector.ConsoleMessage.MessageType.Trace)
162 this._populateStackTraceTreeElement(root);
163 this._formattedMessage = ol;
168 // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
169 this.message = (urlElement ? urlElement.textContent + " " : "") + messageText.textContent;
172 _linkifyLocation: function(url, lineNumber, columnNumber)
174 // FIXME(62725): stack trace line/column numbers are one-based.
175 lineNumber = lineNumber ? lineNumber - 1 : undefined;
176 columnNumber = columnNumber ? columnNumber - 1 : 0;
177 return WebInspector.debuggerPresentationModel.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
180 _linkifyCallFrame: function(callFrame)
182 return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
185 isErrorOrWarning: function()
187 return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
190 _format: function(parameters)
192 // This node is used like a Builder. Values are continually appended onto it.
193 var formattedResult = document.createElement("span");
194 if (!parameters.length)
195 return formattedResult;
197 // Formatting code below assumes that parameters are all wrappers whereas frontend console
198 // API allows passing arbitrary values as messages (strings, numbers, etc.). Wrap them here.
199 for (var i = 0; i < parameters.length; ++i) {
200 // FIXME: Only pass runtime wrappers here.
201 if (parameters[i] instanceof WebInspector.RemoteObject)
204 if (typeof parameters[i] === "object")
205 parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]);
207 parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]);
210 // There can be string log and string eval result. We distinguish between them based on message type.
211 var shouldFormatMessage = WebInspector.RemoteObject.type(parameters[0]) === "string" && this.type !== WebInspector.ConsoleMessage.MessageType.Result;
213 // Multiple parameters with the first being a format string. Save unused substitutions.
214 if (shouldFormatMessage) {
215 // Multiple parameters with the first being a format string. Save unused substitutions.
216 var result = this._formatWithSubstitutionString(parameters, formattedResult);
217 parameters = result.unusedSubstitutions;
218 if (parameters.length)
219 formattedResult.appendChild(document.createTextNode(" "));
222 // Single parameter, or unused substitutions from above.
223 for (var i = 0; i < parameters.length; ++i) {
224 // Inline strings when formatting.
225 if (shouldFormatMessage && parameters[i].type === "string")
226 formattedResult.appendChild(document.createTextNode(parameters[i].description));
228 formattedResult.appendChild(WebInspector.consoleView._format(parameters[i]));
229 if (i < parameters.length - 1)
230 formattedResult.appendChild(document.createTextNode(" "));
232 return formattedResult;
235 _formatWithSubstitutionString: function(parameters, formattedResult)
239 function consoleFormatWrapper(force)
241 return function(obj) {
242 return WebInspector.consoleView._format(obj, force);
246 function valueFormatter(obj)
248 return obj.description;
251 // Firebug uses %o for formatting objects.
252 formatters.o = consoleFormatWrapper(false);
253 formatters.s = valueFormatter;
254 formatters.f = valueFormatter;
255 // Firebug allows both %i and %d for formatting integers.
256 formatters.i = valueFormatter;
257 formatters.d = valueFormatter;
259 // Support %O to force object formatting, instead of the type-based %o formatting.
260 formatters.O = consoleFormatWrapper(true);
262 function append(a, b)
264 if (!(b instanceof Node))
265 a.appendChild(WebInspector.linkifyStringAsFragment(b.toString()));
271 // String.format does treat formattedResult like a Builder, result is an object.
272 return String.format(parameters[0].description, parameters.slice(1), formatters, formattedResult, append);
275 clearHighlight: function()
277 var highlightedMessage = this._formattedMessage;
278 delete this._formattedMessage;
279 this._formatMessage();
280 this._element.replaceChild(this._formattedMessage, highlightedMessage);
283 highlightSearchResults: function(regexObject)
285 regexObject.lastIndex = 0;
286 var text = this.message;
287 var match = regexObject.exec(text);
289 var matchRanges = [];
291 matchRanges.push({ offset: match.index, length: match[0].length });
292 match = regexObject.exec(text);
294 highlightSearchResults(this._formattedMessage, matchRanges);
295 this._element.scrollIntoViewIfNeeded();
298 matchesRegex: function(regexObject)
300 return regexObject.test(this.message);
303 toMessageElement: function()
306 return this._element;
308 var element = document.createElement("div");
309 element.message = this;
310 element.className = "console-message";
312 this._element = element;
314 switch (this.level) {
315 case WebInspector.ConsoleMessage.MessageLevel.Tip:
316 element.addStyleClass("console-tip-level");
318 case WebInspector.ConsoleMessage.MessageLevel.Log:
319 element.addStyleClass("console-log-level");
321 case WebInspector.ConsoleMessage.MessageLevel.Debug:
322 element.addStyleClass("console-debug-level");
324 case WebInspector.ConsoleMessage.MessageLevel.Warning:
325 element.addStyleClass("console-warning-level");
327 case WebInspector.ConsoleMessage.MessageLevel.Error:
328 element.addStyleClass("console-error-level");
332 if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
333 element.addStyleClass("console-group-title");
335 element.appendChild(this._formattedMessage);
337 if (this.repeatCount > 1)
338 this._updateRepeatCount();
343 _populateStackTraceTreeElement: function(parentTreeElement)
345 for (var i = 0; i < this._stackTrace.length; i++) {
346 var frame = this._stackTrace[i];
348 var content = document.createElement("div");
349 var messageTextElement = document.createElement("span");
350 messageTextElement.className = "console-message-text source-code";
351 var functionName = frame.functionName || WebInspector.UIString("(anonymous function)");
352 messageTextElement.appendChild(document.createTextNode(functionName));
353 content.appendChild(messageTextElement);
356 var urlElement = this._linkifyCallFrame(frame);
357 content.appendChild(urlElement);
360 var treeElement = new TreeElement(content);
361 parentTreeElement.appendChild(treeElement);
365 _updateRepeatCount: function() {
366 if (!this.repeatCountElement) {
367 this.repeatCountElement = document.createElement("span");
368 this.repeatCountElement.className = "bubble";
370 this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
371 this._element.addStyleClass("repeated-message");
373 this.repeatCountElement.textContent = this.repeatCount;
379 switch (this.source) {
380 case WebInspector.ConsoleMessage.MessageSource.HTML:
381 sourceString = "HTML";
383 case WebInspector.ConsoleMessage.MessageSource.XML:
384 sourceString = "XML";
386 case WebInspector.ConsoleMessage.MessageSource.JS:
389 case WebInspector.ConsoleMessage.MessageSource.CSS:
390 sourceString = "CSS";
392 case WebInspector.ConsoleMessage.MessageSource.Other:
393 sourceString = "Other";
399 case WebInspector.ConsoleMessage.MessageType.Log:
400 case WebInspector.ConsoleMessage.MessageType.UncaughtException:
401 case WebInspector.ConsoleMessage.MessageType.NetworkError:
404 case WebInspector.ConsoleMessage.MessageType.Object:
405 typeString = "Object";
407 case WebInspector.ConsoleMessage.MessageType.Trace:
408 typeString = "Trace";
410 case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
411 case WebInspector.ConsoleMessage.MessageType.StartGroup:
412 typeString = "Start Group";
414 case WebInspector.ConsoleMessage.MessageType.EndGroup:
415 typeString = "End Group";
417 case WebInspector.ConsoleMessage.MessageType.Assert:
418 typeString = "Assert";
420 case WebInspector.ConsoleMessage.MessageType.Result:
421 typeString = "Result";
426 switch (this.level) {
427 case WebInspector.ConsoleMessage.MessageLevel.Tip:
430 case WebInspector.ConsoleMessage.MessageLevel.Log:
433 case WebInspector.ConsoleMessage.MessageLevel.Warning:
434 levelString = "Warning";
436 case WebInspector.ConsoleMessage.MessageLevel.Debug:
437 levelString = "Debug";
439 case WebInspector.ConsoleMessage.MessageLevel.Error:
440 levelString = "Error";
444 return sourceString + " " + typeString + " " + levelString + ": " + this._formattedMessage.textContent + "\n" + this.url + " line " + this.line;
449 return this._messageText;
452 isEqual: function(msg)
457 if (this._stackTrace) {
458 if (!msg._stackTrace)
460 var l = this._stackTrace;
461 var r = msg._stackTrace;
462 for (var i = 0; i < l.length; i++) {
463 if (l[i].url !== r[i].url ||
464 l[i].functionName !== r[i].functionName ||
465 l[i].lineNumber !== r[i].lineNumber ||
466 l[i].columnNumber !== r[i].columnNumber)
471 return (this.source === msg.source)
472 && (this.type === msg.type)
473 && (this.level === msg.level)
474 && (this.line === msg.line)
475 && (this.url === msg.url)
476 && (this.message === msg.message)
477 && (this._request === msg._request);
482 return this._stackTrace;
486 // Note: Keep these constants in sync with the ones in Console.h
487 WebInspector.ConsoleMessage.MessageSource = {
495 WebInspector.ConsoleMessage.MessageType = {
499 StartGroup: "startGroup",
500 StartGroupCollapsed: "startGroupCollapsed",
501 EndGroup: "endGroup",
503 UncaughtException: "uncaughtException",
504 NetworkError: "networkError",
508 WebInspector.ConsoleMessage.MessageLevel = {