initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / ConsoleMessage.js
1 /*
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
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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.
18  *
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.
29  */
30
31 /**
32  * @constructor
33  */
34 WebInspector.ConsoleStackFrame = function()
35 {
36     this.url = undefined;
37     this.functionName = undefined;
38     this.lineNumber = undefined;
39     this.columnNumber = undefined;
40 }
41
42 /**
43  * @constructor
44  * @param {Array.<WebInspector.ConsoleStackFrame>=} stackTrace
45  * @param {number=} request
46  */
47 WebInspector.ConsoleMessage = function(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request)
48 {
49     this.source = source;
50     this.type = type;
51     this.level = level;
52     this.line = line;
53     this.url = url;
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;
61
62     if (stackTrace && stackTrace.length) {
63         var topCallFrame = stackTrace[0];
64         if (!this.url)
65             this.url = topCallFrame.url;
66         if (!this.line)
67             this.line = topCallFrame.lineNumber;
68     }
69
70     this._formatMessage();
71 }
72
73 WebInspector.ConsoleMessage.createTextMessage = function(text, level)
74 {
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);
77 }
78
79 WebInspector.ConsoleMessage.prototype = {
80     _formatMessage: function()
81     {
82         var stackTrace = this._stackTrace;
83
84         this._formattedMessage = document.createElement("span");
85         this._formattedMessage.className = "console-message-text source-code";
86
87         var messageText;
88         switch (this.type) {
89             case WebInspector.ConsoleMessage.MessageType.Trace:
90                 messageText = document.createTextNode("console.trace()");
91                 break;
92             case WebInspector.ConsoleMessage.MessageType.UncaughtException:
93                 messageText = document.createTextNode(this._messageText);
94                 break;
95             case WebInspector.ConsoleMessage.MessageType.NetworkError:
96                 if (this._request) {
97                     stackTrace = this._request.stackTrace;
98
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));
107                     else
108                         messageText.appendChild(document.createTextNode(" " + this._request.statusCode + " (" + this._request.statusText + ")"));
109                 } else {
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]);
114                 }
115                 break;
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);
121                 break;
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);
126                 break;
127             default:
128                 var args = this._parameters || [this._messageText];
129                 messageText = this._format(args);
130                 break;
131         }
132
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);
140             }
141         }
142
143         this._formattedMessage.appendChild(messageText);
144
145         if (this._stackTrace) {
146             switch (this.type) {
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);
154
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)
160                         root.expand();
161
162                     this._populateStackTraceTreeElement(root);
163                     this._formattedMessage = ol;
164                 }
165             }
166         }
167
168         // This is used for inline message bubbles in SourceFrames, or other plain-text representations.
169         this.message = (urlElement ? urlElement.textContent + " " : "") + messageText.textContent;
170     },
171
172     _linkifyLocation: function(url, lineNumber, columnNumber)
173     {
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");
178     },
179
180     _linkifyCallFrame: function(callFrame)
181     {
182         return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
183     },
184
185     isErrorOrWarning: function()
186     {
187         return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error);
188     },
189
190     _format: function(parameters)
191     {
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;
196
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)
202                 continue;
203
204             if (typeof parameters[i] === "object")
205                 parameters[i] = WebInspector.RemoteObject.fromPayload(parameters[i]);
206             else
207                 parameters[i] = WebInspector.RemoteObject.fromPrimitiveValue(parameters[i]);
208         }
209
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;
212
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(" "));
220         }
221
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));
227             else
228                 formattedResult.appendChild(WebInspector.consoleView._format(parameters[i]));
229             if (i < parameters.length - 1)
230                 formattedResult.appendChild(document.createTextNode(" "));
231         }
232         return formattedResult;
233     },
234
235     _formatWithSubstitutionString: function(parameters, formattedResult)
236     {
237         var formatters = {}
238
239         function consoleFormatWrapper(force)
240         {
241             return function(obj) {
242                 return WebInspector.consoleView._format(obj, force);
243             };
244         }
245
246         function valueFormatter(obj)
247         {
248             return obj.description;
249         }
250
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;
258
259         // Support %O to force object formatting, instead of the type-based %o formatting.
260         formatters.O = consoleFormatWrapper(true);
261
262         function append(a, b)
263         {
264             if (!(b instanceof Node))
265                 a.appendChild(WebInspector.linkifyStringAsFragment(b.toString()));
266             else
267                 a.appendChild(b);
268             return a;
269         }
270
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);
273     },
274
275     clearHighlight: function()
276     {
277         var highlightedMessage = this._formattedMessage;
278         delete this._formattedMessage;
279         this._formatMessage();
280         this._element.replaceChild(this._formattedMessage, highlightedMessage);
281     },
282
283     highlightSearchResults: function(regexObject)
284     {
285         regexObject.lastIndex = 0;
286         var text = this.message;
287         var match = regexObject.exec(text);
288         var offset = 0;
289         var matchRanges = [];
290         while (match) {
291             matchRanges.push({ offset: match.index, length: match[0].length });
292             match = regexObject.exec(text);
293         }
294         highlightSearchResults(this._formattedMessage, matchRanges);
295         this._element.scrollIntoViewIfNeeded();
296     },
297
298     matchesRegex: function(regexObject)
299     {
300         return regexObject.test(this.message);
301     },
302
303     toMessageElement: function()
304     {
305         if (this._element)
306             return this._element;
307
308         var element = document.createElement("div");
309         element.message = this;
310         element.className = "console-message";
311
312         this._element = element;
313
314         switch (this.level) {
315             case WebInspector.ConsoleMessage.MessageLevel.Tip:
316                 element.addStyleClass("console-tip-level");
317                 break;
318             case WebInspector.ConsoleMessage.MessageLevel.Log:
319                 element.addStyleClass("console-log-level");
320                 break;
321             case WebInspector.ConsoleMessage.MessageLevel.Debug:
322                 element.addStyleClass("console-debug-level");
323                 break;
324             case WebInspector.ConsoleMessage.MessageLevel.Warning:
325                 element.addStyleClass("console-warning-level");
326                 break;
327             case WebInspector.ConsoleMessage.MessageLevel.Error:
328                 element.addStyleClass("console-error-level");
329                 break;
330         }
331
332         if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup || this.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
333             element.addStyleClass("console-group-title");
334
335         element.appendChild(this._formattedMessage);
336
337         if (this.repeatCount > 1)
338             this._updateRepeatCount();
339
340         return element;
341     },
342
343     _populateStackTraceTreeElement: function(parentTreeElement)
344     {
345         for (var i = 0; i < this._stackTrace.length; i++) {
346             var frame = this._stackTrace[i];
347
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);
354
355             if (frame.url) {
356                 var urlElement = this._linkifyCallFrame(frame);
357                 content.appendChild(urlElement);
358             }
359
360             var treeElement = new TreeElement(content);
361             parentTreeElement.appendChild(treeElement);
362         }
363     },
364
365     _updateRepeatCount: function() {
366         if (!this.repeatCountElement) {
367             this.repeatCountElement = document.createElement("span");
368             this.repeatCountElement.className = "bubble";
369
370             this._element.insertBefore(this.repeatCountElement, this._element.firstChild);
371             this._element.addStyleClass("repeated-message");
372         }
373         this.repeatCountElement.textContent = this.repeatCount;
374     },
375
376     toString: function()
377     {
378         var sourceString;
379         switch (this.source) {
380             case WebInspector.ConsoleMessage.MessageSource.HTML:
381                 sourceString = "HTML";
382                 break;
383             case WebInspector.ConsoleMessage.MessageSource.XML:
384                 sourceString = "XML";
385                 break;
386             case WebInspector.ConsoleMessage.MessageSource.JS:
387                 sourceString = "JS";
388                 break;
389             case WebInspector.ConsoleMessage.MessageSource.CSS:
390                 sourceString = "CSS";
391                 break;
392             case WebInspector.ConsoleMessage.MessageSource.Other:
393                 sourceString = "Other";
394                 break;
395         }
396
397         var typeString;
398         switch (this.type) {
399             case WebInspector.ConsoleMessage.MessageType.Log:
400             case WebInspector.ConsoleMessage.MessageType.UncaughtException:
401             case WebInspector.ConsoleMessage.MessageType.NetworkError:
402                 typeString = "Log";
403                 break;
404             case WebInspector.ConsoleMessage.MessageType.Object:
405                 typeString = "Object";
406                 break;
407             case WebInspector.ConsoleMessage.MessageType.Trace:
408                 typeString = "Trace";
409                 break;
410             case WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed:
411             case WebInspector.ConsoleMessage.MessageType.StartGroup:
412                 typeString = "Start Group";
413                 break;
414             case WebInspector.ConsoleMessage.MessageType.EndGroup:
415                 typeString = "End Group";
416                 break;
417             case WebInspector.ConsoleMessage.MessageType.Assert:
418                 typeString = "Assert";
419                 break;
420             case WebInspector.ConsoleMessage.MessageType.Result:
421                 typeString = "Result";
422                 break;
423         }
424
425         var levelString;
426         switch (this.level) {
427             case WebInspector.ConsoleMessage.MessageLevel.Tip:
428                 levelString = "Tip";
429                 break;
430             case WebInspector.ConsoleMessage.MessageLevel.Log:
431                 levelString = "Log";
432                 break;
433             case WebInspector.ConsoleMessage.MessageLevel.Warning:
434                 levelString = "Warning";
435                 break;
436             case WebInspector.ConsoleMessage.MessageLevel.Debug:
437                 levelString = "Debug";
438                 break;
439             case WebInspector.ConsoleMessage.MessageLevel.Error:
440                 levelString = "Error";
441                 break;
442         }
443
444         return sourceString + " " + typeString + " " + levelString + ": " + this._formattedMessage.textContent + "\n" + this.url + " line " + this.line;
445     },
446
447     get text()
448     {
449         return this._messageText;
450     },
451
452     isEqual: function(msg)
453     {
454         if (!msg)
455             return false;
456
457         if (this._stackTrace) {
458             if (!msg._stackTrace)
459                 return false;
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)
467                     return false;
468             }
469         }
470
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);
478     },
479
480     get stackTrace()
481     {
482         return this._stackTrace;
483     }
484 }
485
486 // Note: Keep these constants in sync with the ones in Console.h
487 WebInspector.ConsoleMessage.MessageSource = {
488     HTML: "html",
489     XML: "xml",
490     JS: "javascript",
491     CSS: "css",
492     Other: "other"
493 }
494
495 WebInspector.ConsoleMessage.MessageType = {
496     Log: "log",
497     Object: "other",
498     Trace: "trace",
499     StartGroup: "startGroup",
500     StartGroupCollapsed: "startGroupCollapsed",
501     EndGroup: "endGroup",
502     Assert: "assert",
503     UncaughtException: "uncaughtException",
504     NetworkError: "networkError",
505     Result: "result"
506 }
507
508 WebInspector.ConsoleMessage.MessageLevel = {
509     Tip: "tip",
510     Log: "log",
511     Warning: "warning",
512     Error: "error",
513     Debug: "debug"
514 }