gecko/mobile/chrome/content/console.js
Matt Brubeck 53b0ef9031 Bug 601029 - Add a context menu with a "copy" command to error console rows [r=mfinkle]
--HG--
extra : rebase_source : 55b82667e7fe96db1836a88ce824cf9926e732b4
2010-10-01 08:27:12 -07:00

314 lines
9.8 KiB
JavaScript

// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Nino D'Aversa <ninodaversa@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let ConsoleView = {
_list: null,
_inited: false,
_evalTextbox: null,
_evalFrame: null,
_evalCode: "",
_bundle: null,
_showChromeErrors: -1,
init: function cv_init() {
if (this._list)
return;
this._list = document.getElementById("console-box");
this._evalTextbox = document.getElementById("console-eval-textbox");
this._bundle = Elements.browserBundle;
this._count = 0;
this.limit = 250;
let self = this;
let panels = document.getElementById("panel-items");
panels.addEventListener("select",
function(aEvent) {
if (panels.selectedPanel.id == "console-container")
self._delayedInit();
},
false);
},
_delayedInit: function cv__delayedInit() {
if (this._inited)
return;
this._inited = true;
Services.console.registerListener(this);
this.appendInitialItems();
// Delay creation of the iframe for startup performance
this._evalFrame = document.createElement("iframe");
this._evalFrame.id = "console-evaluator";
this._evalFrame.collapsed = true;
document.getElementById("console-container").appendChild(this._evalFrame);
let self = this;
this._evalFrame.addEventListener("load", function() { self.loadOrDisplayResult(); }, true);
},
uninit: function cv_uninit() {
if (this._inited)
Services.console.unregisterListener(this);
},
observe: function(aObject) {
this.appendItem(aObject);
},
showChromeErrors: function() {
if (this._showChromeErrors != -1)
return this._showChromeErrors;
try {
let pref = Services.prefs;
return this._showChromeErrors = pref.getBoolPref("javascript.options.showInConsole");
}
catch(ex) {
return this._showChromeErrors = false;
}
},
appendItem: function cv_appendItem(aObject) {
try {
// Try to QI it to a script error to get more info
let scriptError = aObject.QueryInterface(Ci.nsIScriptError);
// filter chrome urls
if (!this.showChromeErrors && scriptError.sourceName.substr(0, 9) == "chrome://")
return;
this.appendError(scriptError);
}
catch (ex) {
try {
// Try to QI it to a console message
let msg = aObject.QueryInterface(Ci.nsIConsoleMessage);
if (msg.message)
this.appendMessage(msg.message);
else // observed a null/"clear" message
this.clearConsole();
}
catch (ex2) {
// Give up and append the object itself as a string
this.appendMessage(aObject);
}
}
},
appendError: function cv_appendError(aObject) {
let row = this.createConsoleRow();
let nsIScriptError = Ci.nsIScriptError;
// Is this error actually just a non-fatal warning?
let warning = aObject.flags & nsIScriptError.warningFlag != 0;
let typetext = warning ? "typeWarning" : "typeError";
row.setAttribute("typetext", this._bundle.getString(typetext));
row.setAttribute("type", warning ? "warning" : "error");
row.setAttribute("msg", aObject.errorMessage);
row.setAttribute("category", aObject.category);
if (aObject.lineNumber || aObject.sourceName) {
row.setAttribute("href", aObject.sourceName);
row.setAttribute("line", aObject.lineNumber);
}
else {
row.setAttribute("hideSource", "true");
}
if (aObject.sourceLine) {
row.setAttribute("code", aObject.sourceLine.replace(/\s/g, " "));
if (aObject.columnNumber) {
row.setAttribute("col", aObject.columnNumber);
row.setAttribute("errorDots", this.repeatChar(" ", aObject.columnNumber));
row.setAttribute("errorCaret", " ");
}
else {
row.setAttribute("hideCaret", "true");
}
}
else {
row.setAttribute("hideCode", "true");
}
let mode = document.getElementById("console-filter").value;
if (mode != "all" && mode != row.getAttribute("type"))
row.collapsed = true;
this.appendConsoleRow(row);
},
appendMessage: function cv_appendMessage (aMessage) {
let row = this.createConsoleRow();
row.setAttribute("type", "message");
row.setAttribute("msg", aMessage);
let mode = document.getElementById("console-filter").value;
if (mode != "all" && mode != "message")
row.collapsed = true;
this.appendConsoleRow(row);
},
createConsoleRow: function cv_createConsoleRow() {
let row = document.createElement("richlistitem");
row.setAttribute("class", "console-row");
return row;
},
appendConsoleRow: function cv_appendConsoleRow(aRow) {
this._list.appendChild(aRow);
if (++this._count > this.limit)
this.deleteFirst();
},
deleteFirst: function cv_deleteFirst() {
let node = this._list.firstChild;
this._list.removeChild(node);
--this._count;
},
appendInitialItems: function cv_appendInitialItems() {
let out = {}; // Throwaway references to support 'out' parameters.
Services.console.getMessageArray(out, {});
let messages = out.value;
// In case getMessageArray returns 0-length array as null
if (!messages)
messages = [];
let limit = messages.length - this.limit;
if (limit < 0)
limit = 0;
// Checks if console ever been cleared
for (var i = messages.length - 1; i >= limit; --i)
if (!messages[i].message)
break;
// Populate with messages after latest "clear"
while (++i < messages.length)
this.appendItem(messages[i]);
},
clearConsole: function cv_clearConsole() {
if (this._count == 0) // already clear
return;
this._count = 0;
let newRows = this._list.cloneNode(false);
this._list.parentNode.replaceChild(newRows, this._list);
this._list = newRows;
this.selectedItem = null;
},
changeMode: function cv_changeMode() {
let mode = document.getElementById("console-filter").value;
if (this._list.getAttribute("mode") != mode) {
let rows = this._list.childNodes;
for (let i=0; i < rows.length; i++) {
let row = rows[i];
if (mode == "all" || row.getAttribute ("type") == mode)
row.collapsed = false;
else
row.collapsed = true;
}
this._list.mode = mode;
this._list.scrollToIndex(0);
}
},
onContextMenu: function cv_onContextMenu(aEvent) {
let row = aEvent.target;
let text = ["msg", "href", "line", "code", "col"].map(function(attr) row.getAttribute(attr))
.filter(function(x) x).join("\n");
ContextHelper.showPopup({
target: row,
json: {
types: ["copy"],
string: text
}
});
},
onEvalKeyPress: function cv_onEvalKeyPress(aEvent) {
if (aEvent.keyCode == 13)
this.evaluateTypein();
},
onConsoleBoxKeyPress: function cv_onConsoleBoxKeyPress(aEvent) {
if ((aEvent.charCode == 99 || aEvent.charCode == 67) && aEvent.ctrlKey && this._list && this._list.selectedItem) {
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
clipboard.copyString(this._list.selectedItem.getAttribute("msg"));
}
},
evaluateTypein: function cv_evaluateTypein() {
this._evalCode = this._evalTextbox.value;
this.loadOrDisplayResult();
},
loadOrDisplayResult: function cv_loadOrDisplayResult() {
if (this._evalCode) {
this._evalFrame.contentWindow.location = "javascript: " + this._evalCode.replace(/%/g, "%25");
this._evalCode = "";
return;
}
let resultRange = this._evalFrame.contentDocument.createRange();
resultRange.selectNode(this._evalFrame.contentDocument.documentElement);
let result = resultRange.toString();
if (result)
Services.console.logStringMessage(result);
// or could use appendMessage which doesn't persist
},
repeatChar: function cv_repeatChar(aChar, aCol) {
if (--aCol <= 0)
return "";
for (let i = 2; i < aCol; i += i)
aChar += aChar;
return aChar + aChar.slice(0, aCol - aChar.length);
}
};