<?xml version="1.0"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <!DOCTYPE bindings 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="fieldMaxLength" readonly="true"> <!-- Limit displayed string lengths to avoid performance issues. (Bug 796179 and 831020) --> 200 </field> <field name="showChromeErrors" readonly="true"> Services.prefs.getBoolPref("javascript.options.showInConsole"); </field> <property name="count" readonly="true"> <getter>return this.mCount</getter> </property> <property name="mode"> <getter>return this.mMode;</getter> <setter><![CDATA[ if (this.mode != val) { this.mMode = val || "All"; this.setAttribute("mode", this.mMode); this.selectedItem = null; } return val; ]]></setter> </property> <property name="filter"> <getter>return this.mFilter;</getter> <setter><![CDATA[ val = val.toLowerCase(); if (this.mFilter != val) { this.mFilter = val; for (let aRow of this.mConsoleRowBox.children) { this.filterElement(aRow); } } 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; if (val) val.setAttribute("selected", "true"); // Update edit commands window.updateCommands("focus"); return val; ]]></setter> </property> <method name="init"> <body><![CDATA[ this.mCount = 0; this.mConsoleListener = { console: this, observe : function(aObject) { // The message can arrive a little bit after the xbl binding has been // unbind. So node.appendItem will not be available anymore. if ('appendItem' in this.console) this.console.appendItem(aObject); } }; this.mConsoleRowBox = document.getAnonymousElementByAttribute(this, "role", "console-rows"); this.mStrBundle = document.getAnonymousElementByAttribute(this, "role", "string-bundle"); try { Services.console.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.mFilter = ""; this.appendInitialItems(); window.controllers.insertControllerAt(0, this._controller); ]]></body> </method> <method name="destroy"> <body><![CDATA[ Services.console.unregisterListener(this.mConsoleListener); window.controllers.removeController(this._controller); ]]></body> </method> <method name="appendInitialItems"> <body><![CDATA[ var messages = Services.console.getMessageArray(); // 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; // filter private windows if (scriptError.isFromPrivateWindow) 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="_truncateIfNecessary"> <parameter name="aString"/> <parameter name="aMiddleCharacter"/> <body><![CDATA[ if (!aString || aString.length <= this.fieldMaxLength) return {string: aString, column: aMiddleCharacter}; let halfLimit = this.fieldMaxLength / 2; if (!aMiddleCharacter || aMiddleCharacter < 0 || aMiddleCharacter > aString.length) aMiddleCharacter = halfLimit; let startPosition = 0; let endPosition = aString.length; if (aMiddleCharacter - halfLimit >= 0) startPosition = aMiddleCharacter - halfLimit; if (aMiddleCharacter + halfLimit <= aString.length) endPosition = aMiddleCharacter + halfLimit; if (endPosition - startPosition < this.fieldMaxLength) endPosition += this.fieldMaxLength - (endPosition - startPosition); let truncatedString = aString.substring(startPosition, endPosition); let Ci = Components.interfaces; let ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; if (startPosition > 0) { truncatedString = ellipsis + truncatedString; aMiddleCharacter += ellipsis.length; } if (endPosition < aString.length) truncatedString = truncatedString + ellipsis; return { string: truncatedString, column: aMiddleCharacter - startPosition }; ]]></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); row.setAttribute("time", this.properFormatTime(aObject.timeStamp)); if (aObject.lineNumber || aObject.sourceName) { row.setAttribute("href", this._truncateIfNecessary(aObject.sourceName).string); row.mSourceName = aObject.sourceName; row.setAttribute("line", aObject.lineNumber); } else { row.setAttribute("hideSource", "true"); } if (aObject.sourceLine) { let sourceLine = aObject.sourceLine.replace(/\s/g, " "); let truncatedLineObj = this._truncateIfNecessary(sourceLine, aObject.columnNumber); row.setAttribute("code", truncatedLineObj.string); row.mSourceLine = sourceLine; if (aObject.columnNumber) { row.setAttribute("col", aObject.columnNumber); row.setAttribute("errorDots", this.repeatChar(" ", truncatedLineObj.column)); 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) Services.console.logStringMessage(null); Services.console.reset(); ]]></body> </method> <method name="properFormatTime"> <parameter name="aTime"/> <body><![CDATA[ const dateServ = Components.classes["@mozilla.org/intl/scriptabledateformat;1"] .getService(Components.interfaces.nsIScriptableDateFormat); let errorTime = new Date(aTime); return dateServ.FormatDateTime("", dateServ.dateFormatShort, dateServ.timeFormatSeconds, errorTime.getFullYear(), errorTime.getMonth() + 1, errorTime.getDate(), errorTime.getHours(), errorTime.getMinutes(), errorTime.getSeconds()); ]]></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(), document); } 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.filterElement(aRow); 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; this.selectedItem = null; ]]></body> </method> <method name="filterElement"> <parameter name="aRow" /> <body><![CDATA[ let anyMatch = ["msg", "line", "code"].some(function (key) { return (aRow.hasAttribute(key) && this.stringMatchesFilters(aRow.getAttribute(key), this.mFilter)); }, this) || (aRow.mSourceName && this.stringMatchesFilters(aRow.mSourceName, this.mFilter)); if (anyMatch) { aRow.classList.remove("filtered-by-string") } else { aRow.classList.add("filtered-by-string") } ]]></body> </method> <!-- UTILITY FUNCTIONS --> <method name="repeatChar"> <parameter name="aChar"/> <parameter name="aCol"/> <body><![CDATA[ if (--aCol <= 0) return ""; for (var i = 2; i < aCol; i += i) aChar += aChar; return aChar + aChar.slice(0, aCol - aChar.length); ]]></body> </method> <method name="stringMatchesFilters"> <parameter name="aString"/> <parameter name="aFilter"/> <body><![CDATA[ if (!aString || !aFilter) { return true; } let searchStr = aString.toLowerCase(); let filterStrings = aFilter.split(/\s+/); return !filterStrings.some(function (f) { return searchStr.indexOf(f) == -1; }); ]]></body> </method> <constructor> this.init(); </constructor> <destructor> this.destroy(); </destructor> <!-- Command controller for the copy command --> <field name="_controller"><![CDATA[({ _outer: this, QueryInterface: function(aIID) { if (aIID.equals(Components.interfaces.nsIController) || aIID.equals(Components.interfaces.nsISupports)) return this; throw Components.results.NS_NOINTERFACE; }, supportsCommand: function(aCommand) { return aCommand == "cmd_copy"; }, isCommandEnabled: function(aCommand) { return aCommand == "cmd_copy" && this._outer.selectedItem; }, doCommand: function(aCommand) { if (aCommand == "cmd_copy") this._outer.copySelectedItem(); }, onEvent: function() { } });]]></field> </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:label class="label console-time" xbl:inherits="value=time"/> </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"/> <xul: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> <field name="mSourceName">null</field> <field name="mSourceLine">null</field> <method name="toString"> <body><![CDATA[ let msg = ""; let strBundle = this._ConsoleBox.mStrBundle; if (this.hasAttribute("time")) msg += strBundle.getFormattedString("errTime", [this.getAttribute("time")]) + "\n"; msg += this.getAttribute("typetext") + " " + this.getAttribute("msg"); if (this.hasAttribute("line") && this.mSourceName) { msg += "\n" + strBundle.getFormattedString("errFile", [this.mSourceName]) + "\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.mSourceLine; 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="value=href" crop="right"/> </content> <handlers> <handler event="click" phase="capturing" button="0" preventdefault="true"> <![CDATA[ var url = document.getBindingParent(this).mSourceName; url = url.substring(url.lastIndexOf(" ") + 1); var line = getAttribute("line"); gViewSourceUtils.viewSource(url, null, null, line); ]]> </handler> </handlers> </binding> </bindings>