447eacf6a78ce6225f99173067165c45d73ecf97
[WebKit-https.git] / WebCore / page / inspector / ConsolePanel.js
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 WebInspector.ConsolePanel = function()
30 {
31     WebInspector.Panel.call(this);
32
33     this.messages = [];
34
35     this.commandHistory = [];
36     this.commandOffset = 0;
37
38     this.messageList = document.createElement("ol");
39     this.messageList.className = "console-message-list";
40     this.element.appendChild(this.messageList);
41
42     this.messageList.addEventListener("click", this.messageListClicked.bind(this), true);
43
44     this.consolePrompt = document.createElement("textarea");
45     this.consolePrompt.className = "console-prompt";
46     this.element.appendChild(this.consolePrompt);
47
48     this.consolePrompt.addEventListener("keydown", this.promptKeyDown.bind(this), false);
49 }
50
51 WebInspector.ConsolePanel.prototype = {
52     show: function()
53     {
54         WebInspector.Panel.prototype.show.call(this);
55         WebInspector.consoleListItem.select();
56     },
57
58     hide: function()
59     {
60         WebInspector.Panel.prototype.hide.call(this);
61         WebInspector.consoleListItem.deselect();
62     },
63
64     addMessage: function(msg)
65     {
66         if (msg.url in WebInspector.resourceURLMap) {
67             msg.resource = WebInspector.resourceURLMap[msg.url];
68             switch (msg.level) {
69                 case WebInspector.ConsoleMessage.MessageLevel.Warning:
70                     ++msg.resource.warnings;
71                     msg.resource.panel.addMessageToSource(msg);
72                     break;
73                 case WebInspector.ConsoleMessage.MessageLevel.Error:
74                     ++msg.resource.errors;
75                     msg.resource.panel.addMessageToSource(msg);
76                     break;
77             }
78         }
79         this.messages.push(msg);
80
81         var item = msg.toListItem();
82         item.message = msg;
83         this.messageList.appendChild(item);
84         item.scrollIntoView(false);
85     },
86
87     clearMessages: function()
88     {
89         for (var i = 0; i < this.messages.length; ++i) {
90             var resource = this.messages[i].resource;
91             if (!resource)
92                 continue;
93
94             resource.errors = 0;
95             resource.warnings = 0;
96         }
97
98         this.messages = [];
99         this.messageList.removeChildren();
100     },
101
102     messageListClicked: function(event)
103     {
104         var link = event.target.firstParentOrSelfWithNodeName("a");
105         if (link) {
106             WebInspector.updateFocusedNode(link.representedNode);
107             return;
108         }
109
110         var item = event.target.firstParentOrSelfWithNodeName("li");
111         if (!item)
112             return;
113
114         var resource = item.message.resource;
115         if (!resource)
116             return;
117
118         resource.panel.showSourceLine(item.message.line);
119
120         event.stopPropagation();
121         event.preventDefault();
122     },
123
124     promptKeyDown: function(event)
125     {
126         switch (event.keyIdentifier) {
127             case "Enter":
128                 this._onEnterPressed(event);
129                 break;
130             case "Up":
131                 this._onUpPressed(event);
132                 break;
133             case "Down":
134                 this._onDownPressed(event);
135                 break;
136         }
137     },
138
139     _onEnterPressed: function(event)
140     {
141         event.preventDefault();
142         event.stopPropagation();
143
144         var str = this.consolePrompt.value;
145         if (!str.length)
146             return;
147
148         this.commandHistory.push(str);
149         this.commandOffset = 0;
150
151         this.consolePrompt.value = "";
152
153         var result;
154         var exception = false;
155         try {
156             // This with block is needed to work around http://bugs.webkit.org/show_bug.cgi?id=11399
157             with (InspectorController.inspectedWindow()) {
158                 result = eval(str);
159             }
160         } catch(e) {
161             result = e;
162             exception = true;
163         }
164
165         var level = exception ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log;
166
167         this.addMessage(new WebInspector.ConsoleCommand(str, this._outputToNode(result)));
168     },
169
170     _onUpPressed: function(event)
171     {
172         event.preventDefault();
173         event.stopPropagation();
174
175         if (this.commandOffset == this.commandHistory.length)
176             return;
177
178         if (this.commandOffset == 0)
179             this.tempSavedCommand = this.consolePrompt.value;
180
181         ++this.commandOffset;
182         this.consolePrompt.value = this.commandHistory[this.commandHistory.length - this.commandOffset];
183         this.consolePrompt.moveCursorToEnd();
184     },
185
186     _onDownPressed: function(event)
187     {
188         event.preventDefault();
189         event.stopPropagation();
190
191         if (this.commandOffset == 0)
192             return;
193
194         --this.commandOffset;
195
196         if (this.commandOffset == 0) {
197             this.consolePrompt.value = this.tempSavedCommand;
198             this.consolePrompt.moveCursorToEnd();
199             delete this.tempSavedCommand;
200             return;
201         }
202
203         this.consolePrompt.value = this.commandHistory[this.commandHistory.length - this.commandOffset];
204         this.consolePrompt.moveCursorToEnd();
205     },
206
207     _outputToNode: function(output)
208     {
209         if (output instanceof Node) {
210             var anchor = document.createElement("a");
211             anchor.innerHTML = output.titleInfo().title;
212             anchor.representedNode = output;
213             return anchor;
214         }
215         return document.createTextNode(Object.describe(output));
216     }
217 }
218
219 WebInspector.ConsolePanel.prototype.__proto__ = WebInspector.Panel.prototype;
220
221 WebInspector.ConsoleMessage = function(source, level, message, line, url)
222 {
223     this.source = source;
224     this.level = level;
225     this.message = message;
226     this.line = line;
227     this.url = url;
228 }
229
230 WebInspector.ConsoleMessage.prototype = {
231     get shortURL()
232     {
233         if (this.resource)
234             return this.resource.displayName;
235         return this.url;
236     },
237
238     toListItem: function()
239     {
240         var item = document.createElement("li");
241         item.className = "console-message";
242         switch (this.source) {
243             case WebInspector.ConsoleMessage.MessageSource.HTML:
244                 item.className += " console-html-source";
245                 break;
246             case WebInspector.ConsoleMessage.MessageSource.XML:
247                 item.className += " console-xml-source";
248                 break;
249             case WebInspector.ConsoleMessage.MessageSource.JS:
250                 item.className += " console-js-source";
251                 break;
252             case WebInspector.ConsoleMessage.MessageSource.CSS:
253                 item.className += " console-css-source";
254                 break;
255             case WebInspector.ConsoleMessage.MessageSource.Other:
256                 item.className += " console-other-source";
257                 break;
258         }
259
260         switch (this.level) {
261             case WebInspector.ConsoleMessage.MessageLevel.Tip:
262                 item.className += " console-tip-level";
263                 break;
264             case WebInspector.ConsoleMessage.MessageLevel.Log:
265                 item.className += " console-log-level";
266                 break;
267             case WebInspector.ConsoleMessage.MessageLevel.Warning:
268                 item.className += " console-warning-level";
269                 break;
270             case WebInspector.ConsoleMessage.MessageLevel.Error:
271                 item.className += " console-error-level";
272         }
273
274
275         var messageDiv = document.createElement("div");
276         messageDiv.className = "console-message-message";
277         messageDiv.textContent = this.message;
278         item.appendChild(messageDiv);
279
280         var urlDiv = document.createElement("div");
281         urlDiv.className = "console-message-url";
282         urlDiv.textContent = this.url;
283         item.appendChild(urlDiv);
284
285         if (this.line) {
286             var lineDiv = document.createElement("div");
287             lineDiv.className = "console-message-line";
288             lineDiv.textContent = this.line;
289             item.appendChild(lineDiv);
290         }
291
292         return item;
293     },
294
295     toString: function()
296     {
297         var sourceString;
298         switch (this.source) {
299             case WebInspector.ConsoleMessage.MessageSource.HTML:
300                 sourceString = "HTML";
301                 break;
302             case WebInspector.ConsoleMessage.MessageSource.XML:
303                 sourceString = "XML";
304                 break;
305             case WebInspector.ConsoleMessage.MessageSource.JS:
306                 sourceString = "JS";
307                 break;
308             case WebInspector.ConsoleMessage.MessageSource.CSS:
309                 sourceString = "CSS";
310                 break;
311             case WebInspector.ConsoleMessage.MessageSource.Other:
312                 sourceString = "Other";
313                 break;
314         }
315
316         var levelString;
317         switch (this.level) {
318             case WebInspector.ConsoleMessage.MessageLevel.Tip:
319                 levelString = "Tip";
320                 break;
321             case WebInspector.ConsoleMessage.MessageLevel.Log:
322                 levelString = "Log";
323                 break;
324             case WebInspector.ConsoleMessage.MessageLevel.Warning:
325                 levelString = "Warning";
326                 break;
327             case WebInspector.ConsoleMessage.MessageLevel.Error:
328                 levelString = "Error";
329                 break;
330         }
331
332         return sourceString + " " + levelString + ": " + this.message + "\n" + this.url + " line " + this.line;
333     }
334 }
335
336 // Note: Keep these constants in sync with the ones in Chrome.h
337 WebInspector.ConsoleMessage.MessageSource = {
338     HTML: 0,
339     XML: 1,
340     JS: 2,
341     CSS: 3,
342     Other: 4,
343 };
344
345 WebInspector.ConsoleMessage.MessageLevel = {
346     Tip: 0,
347     Log: 1,
348     Warning: 2,
349     Error: 3,
350 };
351
352 WebInspector.ConsoleCommand = function(input, output)
353 {
354     this.input = input;
355     this.output = output;
356 }
357
358 WebInspector.ConsoleCommand.prototype = {
359     toListItem: function()
360     {
361         var item = document.createElement("li");
362         item.className = "console-command";
363
364         var inputDiv = document.createElement("div");
365         inputDiv.className = "console-command-input";
366         inputDiv.textContent = this.input;
367         item.appendChild(inputDiv);
368
369         var outputDiv = document.createElement("div");
370         outputDiv.className = "console-command-output";
371         outputDiv.appendChild(this.output);
372         item.appendChild(outputDiv);
373
374         return item;
375     }
376 }