r=bz sr=bz Perhaps an explanation. String concatenation is a chance for garbage collection. If you have unbounded input, this can be pretty bad. When you start running low on memory, the garbage collector gets desperate and won't take shortcuts. Worst of all, the original code generates one string of garbage every iteration starting with the second or third. This code uses a=[] to construct an array and a[n] to set the upper array boundary, 0 being the lower, and there are n slots between a[0] and a[n]. join() will build a string with n copies of the join argument, where n is the number of slots between between cells. So for: a=[]; a[1]=''; there's 1 slot between [0] and [1]. The array's length is actually 2, but the joined length will be 1. While it is possible to use new Array(x), the behavior is different because the array has length x and there are x-1 spaces between cells. This leads to code which would be both much longer and harder to read. And definitely not particularly intuitive. The code used here otoh, while magical will hopefully require you to pause, think, and quickly understand what's going on. If not, you paused long enough for this bubble to appear and explain it to you :). git-svn-id: svn://10.0.0.236/trunk@207376 18797224-902f-48f8-a5cc-f745e15eee43
398 lines
14 KiB
XML
398 lines
14 KiB
XML
<?xml version="1.0"?>
|
|
|
|
<!DOCTYPE window SYSTEM "chrome://global/locale/console.dtd">
|
|
|
|
<bindings id="consoleBindings"
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
|
|
|
<binding id="console-box" extends="xul:box">
|
|
<content>
|
|
<xul:stringbundle src="chrome://global/locale/console.properties" role="string-bundle"/>
|
|
<xul:vbox class="console-box-internal">
|
|
<xul:vbox class="console-rows" role="console-rows" xbl:inherits="dir=sortOrder"/>
|
|
</xul:vbox>
|
|
</content>
|
|
|
|
<implementation>
|
|
<field name="limit" readonly="true">
|
|
250
|
|
</field>
|
|
<field name="_showChromeErrors">-1</field>
|
|
|
|
<property name="showChromeErrors">
|
|
<getter><![CDATA[
|
|
if (this._showChromeErrors != -1)
|
|
return this._showChromeErrors;
|
|
var pref = Components.classes['@mozilla.org/preferences-service;1'].getService();
|
|
pref = pref.QueryInterface(Components.interfaces.nsIPrefBranch);
|
|
|
|
try {
|
|
return this._showChromeErrors = pref.getBoolPref("javascript.options.showInConsole");
|
|
}
|
|
catch(ex) {
|
|
return this._showChromeErrors = false;
|
|
}
|
|
]]></getter>
|
|
</property>
|
|
|
|
<property name="count" readonly="true">
|
|
<getter>return this.mCount</getter>
|
|
</property>
|
|
|
|
<property name="mode">
|
|
<getter>return this.mMode;</getter>
|
|
<setter><![CDATA[
|
|
this.mMode = val || "All";
|
|
this.setAttribute("mode", this.mMode);
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<property name="sortOrder">
|
|
<getter>return this.getAttribute("sortOrder");</getter>
|
|
<setter>this.setAttribute("sortOrder", val); return val;</setter>
|
|
</property>
|
|
<field name="mSelectedItem">null</field>
|
|
<property name="selectedItem">
|
|
<getter>return this.mSelectedItem</getter>
|
|
<setter><![CDATA[
|
|
if (this.mSelectedItem)
|
|
this.mSelectedItem.removeAttribute("selected");
|
|
|
|
this.mSelectedItem = val;
|
|
val.setAttribute("selected", "true");
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="init">
|
|
<body><![CDATA[
|
|
this.mCount = 0;
|
|
|
|
this.mConsoleListener = {
|
|
console: this,
|
|
observe : function(aObject) { this.console.appendItem(aObject); }
|
|
};
|
|
|
|
this.mConsoleRowBox = document.getAnonymousElementByAttribute(this, "role", "console-rows");
|
|
this.mStrBundle = document.getAnonymousElementByAttribute(this, "role", "string-bundle");
|
|
|
|
try {
|
|
var isupports = Components.classes['@mozilla.org/consoleservice;1'].getService();
|
|
this.mCService = isupports.QueryInterface(Components.interfaces.nsIConsoleService);
|
|
this.mCService.registerListener(this.mConsoleListener);
|
|
} catch (ex) {
|
|
appendItem(
|
|
"Unable to display errors - couldn't get Console Service component. " +
|
|
"(Missing @mozilla.org/consoleservice;1)");
|
|
return;
|
|
}
|
|
|
|
this.mMode = this.getAttribute("mode") || "All";
|
|
|
|
this.appendInitialItems();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="destroy">
|
|
<body><![CDATA[
|
|
this.mCService.unregisterListener(this.mConsoleListener);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="appendInitialItems">
|
|
<body><![CDATA[
|
|
var out = {}; // Throwaway references to support 'out' parameters.
|
|
this.mCService.getMessageArray(out, {});
|
|
var messages = out.value;
|
|
|
|
// In case getMessageArray returns 0-length array as null
|
|
if (!messages)
|
|
messages = [];
|
|
|
|
var 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]);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="appendItem">
|
|
<parameter name="aObject"/>
|
|
<body><![CDATA[
|
|
try {
|
|
// Try to QI it to a script error to get more info
|
|
var scriptError = aObject.QueryInterface(Components.interfaces.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
|
|
var msg = aObject.QueryInterface(Components.interfaces.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);
|
|
}
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="appendError">
|
|
<parameter name="aObject"/>
|
|
<body><![CDATA[
|
|
var row = this.createConsoleRow();
|
|
var nsIScriptError = Components.interfaces.nsIScriptError;
|
|
|
|
// Is this error actually just a non-fatal warning?
|
|
var warning = aObject.flags & nsIScriptError.warningFlag != 0;
|
|
|
|
var typetext = warning ? "typeWarning" : "typeError";
|
|
row.setAttribute("typetext", this.mStrBundle.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("\n", "", "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");
|
|
}
|
|
this.appendConsoleRow(row);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="appendMessage">
|
|
<parameter name="aMessage"/>
|
|
<parameter name="aType"/>
|
|
<body><![CDATA[
|
|
var row = this.createConsoleRow();
|
|
row.setAttribute("type", aType || "message");
|
|
row.setAttribute("msg", aMessage);
|
|
this.appendConsoleRow(row);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="clear">
|
|
<body><![CDATA[
|
|
// add a "clear" message (mainly for other listeners)
|
|
this.mCService.logStringMessage(null);
|
|
this.mCService.reset();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="copySelectedItem">
|
|
<body><![CDATA[
|
|
if (this.mSelectedItem) try {
|
|
const clipURI = "@mozilla.org/widget/clipboardhelper;1";
|
|
const clipI = Components.interfaces.nsIClipboardHelper;
|
|
var clipboard = Components.classes[clipURI].getService(clipI);
|
|
|
|
clipboard.copyString(this.mSelectedItem.toString());
|
|
} catch (ex) {
|
|
// Unable to copy anything, die quietly
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="createConsoleRow">
|
|
<body><![CDATA[
|
|
var row = document.createElement("box");
|
|
row.setAttribute("class", "console-row");
|
|
row._IsConsoleRow = true;
|
|
row._ConsoleBox = this;
|
|
return row;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="appendConsoleRow">
|
|
<parameter name="aRow"/>
|
|
<body><![CDATA[
|
|
this.mConsoleRowBox.appendChild(aRow);
|
|
if (++this.mCount > this.limit) this.deleteFirst();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="deleteFirst">
|
|
<body><![CDATA[
|
|
var node = this.mConsoleRowBox.firstChild;
|
|
this.mConsoleRowBox.removeChild(node);
|
|
--this.mCount;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="clearConsole">
|
|
<body><![CDATA[
|
|
if (this.mCount == 0) // already clear
|
|
return;
|
|
this.mCount = 0;
|
|
|
|
var newRows = this.mConsoleRowBox.cloneNode(false);
|
|
this.mConsoleRowBox.parentNode.replaceChild(newRows, this.mConsoleRowBox);
|
|
this.mConsoleRowBox = newRows;
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- UTILITY FUNCTIONS -->
|
|
|
|
<method name="repeatChar">
|
|
<parameter name="aChar"/>
|
|
<parameter name="aCol"/>
|
|
<body><![CDATA[
|
|
var ary = [];
|
|
ary[aCol] = '';
|
|
return ary.join(aChar);
|
|
]]></body>
|
|
</method>
|
|
|
|
<constructor> this.init(); </constructor>
|
|
<destructor> this.destroy(); </destructor>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="mousedown"><![CDATA[
|
|
if (event.button == 0 || event.button == 2) {
|
|
var target = event.originalTarget;
|
|
|
|
while (target && !("_IsConsoleRow" in target))
|
|
target = target.parentNode;
|
|
|
|
if (target)
|
|
this.selectedItem = target;
|
|
}
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="error" extends="xul:box">
|
|
<content>
|
|
<xul:box class="console-row-internal-box" flex="1">
|
|
<xul:box class="console-row-icon" align="center" xbl:inherits="selected">
|
|
<xul:image class="console-icon" xbl:inherits="src,type"/>
|
|
</xul:box>
|
|
<xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
|
|
<xul:box class="console-row-msg" align="start">
|
|
<xul:label class="label" xbl:inherits="value=typetext"/>
|
|
<xul:description class="console-error-msg" xbl:inherits="xbl:text=msg" flex="1"/>
|
|
</xul:box>
|
|
<xul:box class="console-row-file" xbl:inherits="hidden=hideSource">
|
|
<xul:label class="label" value="&errFile.label;"/>
|
|
<xul:box class="console-error-source" xbl:inherits="href,line"/>
|
|
<spacer flex="1"/>
|
|
<xul:hbox class="lineNumberRow" xbl:inherits="line">
|
|
<xul:label class="label" value="&errLine.label;"/>
|
|
<xul:label class="label" xbl:inherits="value=line"/>
|
|
</xul:hbox>
|
|
</xul:box>
|
|
<xul:vbox class="console-row-code" xbl:inherits="selected,hidden=hideCode">
|
|
<xul:label class="monospace console-code" xbl:inherits="value=code" crop="end"/>
|
|
<xul:box xbl:inherits="hidden=hideCaret">
|
|
<xul:label class="monospace console-dots" xbl:inherits="value=errorDots"/>
|
|
<xul:label class="monospace console-caret" xbl:inherits="value=errorCaret"/>
|
|
<xul:spacer flex="1"/>
|
|
</xul:box>
|
|
</xul:vbox>
|
|
</xul:vbox>
|
|
</xul:box>
|
|
</content>
|
|
|
|
<implementation>
|
|
|
|
<method name="toString">
|
|
<body><![CDATA[
|
|
var msg = this.getAttribute("typetext") + " " + this.getAttribute("msg");
|
|
|
|
var strBundle = this._ConsoleBox.mStrBundle;
|
|
|
|
if (this.hasAttribute("line") && this.hasAttribute("href")) {
|
|
msg += "\n" + strBundle.getFormattedString("errFile",
|
|
[this.getAttribute("href")]) + "\n";
|
|
if (this.hasAttribute("col")) {
|
|
msg += strBundle.getFormattedString("errLineCol",
|
|
[this.getAttribute("line"), this.getAttribute("col")]);
|
|
} else
|
|
msg += strBundle.getFormattedString("errLine", [this.getAttribute("line")]);
|
|
}
|
|
|
|
if (this.hasAttribute("code"))
|
|
msg += "\n" + strBundle.getString("errCode") + "\n" + this.getAttribute("code");
|
|
|
|
return msg;
|
|
]]></body>
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
</binding>
|
|
|
|
<binding id="message" extends="xul:box">
|
|
<content>
|
|
<xul:box class="console-internal-box" flex="1">
|
|
<xul:box class="console-row-icon" align="center">
|
|
<xul:image class="console-icon" xbl:inherits="src,type"/>
|
|
</xul:box>
|
|
<xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
|
|
<xul:vbox class="console-row-msg" flex="1">
|
|
<xul:description class="console-msg-text" xbl:inherits="xbl:text=msg"/>
|
|
</xul:vbox>
|
|
</xul:vbox>
|
|
</xul:box>
|
|
</content>
|
|
|
|
<implementation>
|
|
<method name="toString">
|
|
<body><![CDATA[
|
|
return this.getAttribute("msg");
|
|
]]></body>
|
|
</method>
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="console-error-source" extends="xul:box">
|
|
<content>
|
|
<xul:label class="text-link" xbl:inherits="href,value=href" crop="right"/>
|
|
</content>
|
|
|
|
<handlers>
|
|
<handler event="click" phase="capturing" button="0" preventdefault="true">
|
|
<![CDATA[
|
|
var url = this.getAttribute("href");
|
|
var line = getAttribute("line");
|
|
window.openDialog(
|
|
"chrome://global/content/viewSource.xul", "_blank",
|
|
"all,dialog=no", url, null, null, line);
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
</bindings>
|
|
|