Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 let ConsolePanelView = {
7 _list: null,
8 _inited: false,
9 _evalTextbox: null,
10 _evalFrame: null,
11 _evalCode: "",
12 _bundle: null,
13 _showChromeErrors: -1,
14 _enabledPref: "devtools.errorconsole.enabled",
16 get enabled() {
17 return Services.prefs.getBoolPref(this._enabledPref);
18 },
20 get follow() {
21 return document.getElementById("console-follow-checkbox").checked;
22 },
24 init: function cv_init() {
25 if (this._list)
26 return;
28 this._list = document.getElementById("console-box");
29 this._evalTextbox = document.getElementById("console-eval-textbox");
30 this._bundle = Strings.browser;
32 this._count = 0;
33 this.limit = 250;
34 this.fieldMaxLength = 140;
36 try {
37 // update users using the legacy pref
38 if (Services.prefs.getBoolPref("browser.console.showInPanel")) {
39 Services.prefs.setBoolPref(this._enabledPref, true);
40 Services.prefs.clearUserPref("browser.console.showInPanel");
41 }
42 } catch(ex) {
43 // likely don't have an old pref
44 }
45 Services.prefs.addObserver(this._enabledPref, this, false);
46 },
48 show: function show() {
49 if (this._inited)
50 return;
51 this._inited = true;
53 this.init(); // In case the panel is selected before init has been called.
55 Services.console.registerListener(this);
57 this.appendInitialItems();
59 // Delay creation of the iframe for startup performance
60 this._evalFrame = document.createElement("iframe");
61 this._evalFrame.id = "console-evaluator";
62 this._evalFrame.collapsed = true;
63 document.getElementById("console-container").appendChild(this._evalFrame);
65 this._evalFrame.addEventListener("load", this.loadOrDisplayResult.bind(this), true);
66 },
68 uninit: function cv_uninit() {
69 if (this._inited)
70 Services.console.unregisterListener(this);
72 Services.prefs.removeObserver(this._enabledPref, this, false);
73 },
75 observe: function(aSubject, aTopic, aData) {
76 if (aTopic == "nsPref:changed") {
77 // We may choose to create a new menu in v2
78 }
79 else
80 this.appendItem(aSubject);
81 },
83 showChromeErrors: function() {
84 if (this._showChromeErrors != -1)
85 return this._showChromeErrors;
87 try {
88 let pref = Services.prefs;
89 return this._showChromeErrors = pref.getBoolPref("javascript.options.showInConsole");
90 }
91 catch(ex) {
92 return this._showChromeErrors = false;
93 }
94 },
96 appendItem: function cv_appendItem(aObject) {
97 let index = -1;
98 try {
99 // Try to QI it to a script error to get more info
100 let scriptError = aObject.QueryInterface(Ci.nsIScriptError);
102 // filter chrome urls
103 if (!this.showChromeErrors && scriptError.sourceName.substr(0, 9) == "chrome://")
104 return;
105 index = this.appendError(scriptError);
106 }
107 catch (ex) {
108 try {
109 // Try to QI it to a console message
110 let msg = aObject.QueryInterface(Ci.nsIConsoleMessage);
112 if (msg.message)
113 index = this.appendMessage(msg.message);
114 else // observed a null/"clear" message
115 this.clearConsole();
116 }
117 catch (ex2) {
118 // Give up and append the object itself as a string
119 index = this.appendMessage(aObject);
120 }
121 }
122 if (this.follow) {
123 this._list.ensureIndexIsVisible(index);
124 }
125 },
127 truncateIfNecessary: function (aString) {
128 if (!aString || aString.length <= this.fieldMaxLength) {
129 return aString;
130 }
131 let truncatedString = aString.substring(0, this.fieldMaxLength);
132 let Ci = Components.interfaces;
133 let ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
134 Ci.nsIPrefLocalizedString).data;
135 truncatedString = truncatedString + ellipsis;
136 return truncatedString;
137 },
139 appendError: function cv_appendError(aObject) {
140 let row = this.createConsoleRow();
141 let nsIScriptError = Ci.nsIScriptError;
143 // Is this error actually just a non-fatal warning?
144 let warning = aObject.flags & nsIScriptError.warningFlag != 0;
146 let typetext = warning ? "typeWarning" : "typeError";
147 row.setAttribute("typetext", this._bundle.GetStringFromName(typetext));
148 row.setAttribute("type", warning ? "warning" : "error");
149 row.setAttribute("msg", aObject.errorMessage);
150 row.setAttribute("category", aObject.category);
151 if (aObject.lineNumber || aObject.sourceName) {
152 row.setAttribute("href", aObject.sourceName);
153 row.setAttribute("line", aObject.lineNumber);
154 }
155 else {
156 row.setAttribute("hideSource", "true");
157 }
158 // hide code by default, otherwise initial item display will
159 // hang the browser.
160 row.setAttribute("hideCode", "true");
161 row.setAttribute("hideCaret", "true");
163 if (aObject.sourceLine) {
164 row.setAttribute("code", this.truncateIfNecessary(aObject.sourceLine.replace(/\s/g, " ")));
165 if (aObject.columnNumber) {
166 row.setAttribute("col", aObject.columnNumber);
167 }
168 }
170 let mode = document.getElementById("console-filter").value;
171 if (mode != "all" && mode != row.getAttribute("type")) {
172 row.collapsed = true;
173 }
175 row.setAttribute("onclick", "ConsolePanelView.onRowClick(this)");
176 this.appendConsoleRow(row);
177 return this._list.getIndexOfItem(row);
178 },
180 appendMessage: function cv_appendMessage (aMessage) {
181 let row = this.createConsoleRow();
182 row.setAttribute("type", "message");
183 row.setAttribute("msg", aMessage);
185 let mode = document.getElementById("console-filter").value;
186 if (mode != "all" && mode != "message")
187 row.collapsed = true;
189 this.appendConsoleRow(row);
190 return this._list.getIndexOfItem(row);
191 },
193 createConsoleRow: function cv_createConsoleRow() {
194 let row = document.createElement("richlistitem");
195 row.setAttribute("class", "console-row");
196 return row;
197 },
199 appendConsoleRow: function cv_appendConsoleRow(aRow) {
200 this._list.appendChild(aRow);
201 if (++this._count > this.limit) {
202 this.deleteFirst();
203 }
204 },
206 deleteFirst: function cv_deleteFirst() {
207 let node = this._list.firstChild;
208 this._list.removeChild(node);
209 --this._count;
210 },
212 appendInitialItems: function cv_appendInitialItems() {
213 this._list.collapsed = true;
214 let messages = Services.console.getMessageArray();
216 // In case getMessageArray returns 0-length array as null
217 if (!messages)
218 messages = [];
220 let limit = messages.length - this.limit;
221 if (limit < 0)
222 limit = 0;
224 // Checks if console ever been cleared
225 for (var i = messages.length - 1; i >= limit; --i) {
226 if (!messages[i].message) {
227 break;
228 }
229 }
231 // Populate with messages after latest "clear"
232 while (++i < messages.length) {
233 this.appendItem(messages[i]);
234 }
235 this._list.collapsed = false;
236 },
238 clearConsole: function cv_clearConsole() {
239 if (this._count == 0) // already clear
240 return;
241 this._count = 0;
243 let newRows = this._list.cloneNode(false);
244 this._list.parentNode.replaceChild(newRows, this._list);
245 this._list = newRows;
246 this.selectedItem = null;
247 },
249 copyAll: function () {
250 let mode = document.getElementById("console-filter").value;
251 let rows = this._list.childNodes;
252 let copyText = "";
253 for (let i=0; i < rows.length; i++) {
254 let row = rows[i];
255 if (mode == "all" || row.getAttribute ("type") == mode) {
256 let text = "* " + row.getAttribute("msg");
257 if (row.hasAttribute("href")) {
258 text += "\r\n " + row.getAttribute("href") + " line:" + row.getAttribute("line");
259 }
260 if (row.hasAttribute("code")) {
261 text += "\r\n " + row.getAttribute("code") + " col:" + row.getAttribute("col");
262 }
263 copyText += text + "\r\n";
264 }
265 }
266 let clip = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
267 clip.copyString(copyText, document);
268 },
270 changeMode: function cv_changeMode() {
271 let mode = document.getElementById("console-filter").value;
272 if (this._list.getAttribute("mode") != mode) {
273 let rows = this._list.childNodes;
274 for (let i=0; i < rows.length; i++) {
275 let row = rows[i];
276 if (mode == "all" || row.getAttribute ("type") == mode)
277 row.collapsed = false;
278 else
279 row.collapsed = true;
280 }
281 this._list.mode = mode;
282 this._list.scrollToIndex(0);
283 }
284 },
286 onContextMenu: function cv_onContextMenu(aEvent) {
287 let row = aEvent.target;
288 let text = ["msg", "href", "line", "code", "col"].map(function(attr) row.getAttribute(attr))
289 .filter(function(x) x).join("\r\n");
291 ContextMenuUI.showContextMenu({
292 target: row,
293 json: {
294 types: ["copy"],
295 string: text,
296 xPos: aEvent.clientX,
297 yPos: aEvent.clientY
298 }
299 });
300 },
302 onRowClick: function (aRow) {
303 if (aRow.hasAttribute("code")) {
304 aRow.setAttribute("hideCode", "false");
305 }
306 if (aRow.hasAttribute("col")) {
307 aRow.setAttribute("hideCaret", "false");
308 }
309 },
311 onEvalKeyPress: function cv_onEvalKeyPress(aEvent) {
312 if (aEvent.keyCode == 13)
313 this.evaluateTypein();
314 },
316 onConsoleBoxKeyPress: function cv_onConsoleBoxKeyPress(aEvent) {
317 if ((aEvent.charCode == 99 || aEvent.charCode == 67) && aEvent.ctrlKey && this._list && this._list.selectedItem) {
318 let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
319 clipboard.copyString(this._list.selectedItem.getAttribute("msg"), document);
320 }
321 },
323 evaluateTypein: function cv_evaluateTypein() {
324 this._evalCode = this._evalTextbox.value;
325 this.loadOrDisplayResult();
326 },
328 loadOrDisplayResult: function cv_loadOrDisplayResult() {
329 if (this._evalCode) {
330 this._evalFrame.contentWindow.location = "javascript: " + this._evalCode.replace(/%/g, "%25");
331 this._evalCode = "";
332 return;
333 }
335 let resultRange = this._evalFrame.contentDocument.createRange();
336 resultRange.selectNode(this._evalFrame.contentDocument.documentElement);
337 let result = resultRange.toString();
338 if (result)
339 Services.console.logStringMessage(result);
340 // or could use appendMessage which doesn't persist
341 },
343 repeatChar: function cv_repeatChar(aChar, aCol) {
344 if (--aCol <= 0)
345 return "";
347 for (let i = 2; i < aCol; i += i)
348 aChar += aChar;
350 return aChar + aChar.slice(0, aCol - aChar.length);
351 }
352 };