diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 07:11:37 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 07:11:37 -0500 |
commit | 464e29230eeb8c25dfb064f14239b1288c85332b (patch) | |
tree | 08935287f435060c489af48cd4a59d00e76197c6 /toolkit/components/console | |
parent | 6d614170cbfa958564eb5f824234ad5a9e484344 (diff) | |
download | UXP-464e29230eeb8c25dfb064f14239b1288c85332b.tar UXP-464e29230eeb8c25dfb064f14239b1288c85332b.tar.gz UXP-464e29230eeb8c25dfb064f14239b1288c85332b.tar.lz UXP-464e29230eeb8c25dfb064f14239b1288c85332b.tar.xz UXP-464e29230eeb8c25dfb064f14239b1288c85332b.zip |
Issue N/A - Restore the Toolkit Error Console - Part 1: Toolkit
Diffstat (limited to 'toolkit/components/console')
-rw-r--r-- | toolkit/components/console/content/console.css | 74 | ||||
-rw-r--r-- | toolkit/components/console/content/console.js | 111 | ||||
-rw-r--r-- | toolkit/components/console/content/console.xul | 101 | ||||
-rw-r--r-- | toolkit/components/console/content/consoleBindings.xml | 547 | ||||
-rw-r--r-- | toolkit/components/console/jar.mn | 9 | ||||
-rw-r--r-- | toolkit/components/console/jsconsole-clhandler.js | 40 | ||||
-rw-r--r-- | toolkit/components/console/jsconsole-clhandler.manifest | 3 | ||||
-rw-r--r-- | toolkit/components/console/moz.build | 14 | ||||
-rw-r--r-- | toolkit/components/console/tests/chrome.ini | 3 | ||||
-rw-r--r-- | toolkit/components/console/tests/test_hugeURIs.xul | 64 |
10 files changed, 966 insertions, 0 deletions
diff --git a/toolkit/components/console/content/console.css b/toolkit/components/console/content/console.css new file mode 100644 index 000000000..e05f13c3e --- /dev/null +++ b/toolkit/components/console/content/console.css @@ -0,0 +1,74 @@ +/* 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/. */ + + +.console-box { + -moz-binding: url("chrome://global/content/consoleBindings.xml#console-box"); + overflow: auto; +} + +.console-rows { + -moz-user-focus: normal; +} + +.console-row[type="error"], +.console-row[type="warning"] { + -moz-binding: url("chrome://global/content/consoleBindings.xml#error"); +} + +.console-row[type="message"] { + -moz-binding: url("chrome://global/content/consoleBindings.xml#message"); +} + +.console-msg-text, +.console-error-msg { + white-space: pre-wrap; +} + +.console-error-source { + -moz-binding: url("chrome://global/content/consoleBindings.xml#console-error-source"); +} + +.console-dots { + width: 1px; +} + +/* :::::::::: hiding and showing of rows for each mode :::::::::: */ + +.console-box[mode="Warnings"] > .console-box-internal > .console-rows + > .console-row[type="error"], +.console-box[mode="Messages"] > .console-box-internal > .console-rows + > .console-row[type="error"] +{ + display: none; +} + +.console-box[mode="Errors"] > .console-box-internal > .console-rows + > .console-row[type="warning"], +.console-box[mode="Messages"] > .console-box-internal > .console-rows + > .console-row[type="warning"] +{ + display: none; +} + +.console-box[mode="Errors"] > .console-box-internal > .console-rows + > .console-row[type="message"], +.console-box[mode="Warnings"] > .console-box-internal > .console-rows + > .console-row[type="message"] +{ + display: none; +} + +.filtered-by-string { + display: none; +} + +/* If line number is 0, hide the line number section */ +.lineNumberRow[line="0"] { + display: none; +} + +#TextboxEval { + direction: ltr; +} diff --git a/toolkit/components/console/content/console.js b/toolkit/components/console/content/console.js new file mode 100644 index 000000000..ad6802607 --- /dev/null +++ b/toolkit/components/console/content/console.js @@ -0,0 +1,111 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + +/* 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/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +var gConsole, gConsoleBundle, gTextBoxEval, gEvaluator, gCodeToEvaluate; +var gFilter; + +/* :::::::: Console Initialization ::::::::::::::: */ + +window.onload = function() +{ + gConsole = document.getElementById("ConsoleBox"); + gConsoleBundle = document.getElementById("ConsoleBundle"); + gTextBoxEval = document.getElementById("TextboxEval"); + gEvaluator = document.getElementById("Evaluator"); + gFilter = document.getElementById("Filter"); + + updateSortCommand(gConsole.sortOrder); + updateModeCommand(gConsole.mode); + + gEvaluator.addEventListener("load", loadOrDisplayResult, true); +} + +/* :::::::: Console UI Functions ::::::::::::::: */ + +function changeFilter() +{ + gConsole.filter = gFilter.value; + + document.persist("ConsoleBox", "filter"); +} + +function changeMode(aMode) +{ + switch (aMode) { + case "Errors": + case "Warnings": + case "Messages": + gConsole.mode = aMode; + break; + case "All": + gConsole.mode = null; + } + + document.persist("ConsoleBox", "mode"); +} + +function clearConsole() +{ + gConsole.clear(); +} + +function changeSortOrder(aOrder) +{ + updateSortCommand(gConsole.sortOrder = aOrder); +} + +function updateSortCommand(aOrder) +{ + var orderString = aOrder == 'reverse' ? "Descend" : "Ascend"; + var bc = document.getElementById("Console:sort"+orderString); + bc.setAttribute("checked", true); + + orderString = aOrder == 'reverse' ? "Ascend" : "Descend"; + bc = document.getElementById("Console:sort"+orderString); + bc.setAttribute("checked", false); +} + +function updateModeCommand(aMode) +{ + /* aMode can end up invalid if it set by an extension that replaces */ + /* mode and then it is uninstalled or disabled */ + var bc = document.getElementById("Console:mode" + aMode) || + document.getElementById("Console:modeAll"); + bc.setAttribute("checked", true); +} + +function onEvalKeyPress(aEvent) +{ + if (aEvent.keyCode == 13) + evaluateTypein(); +} + +function evaluateTypein() +{ + gCodeToEvaluate = gTextBoxEval.value; + // reset the iframe first; the code will be evaluated in loadOrDisplayResult + // below, once about:blank has completed loading (see bug 385092) + gEvaluator.contentWindow.location = "about:blank"; +} + +function loadOrDisplayResult() +{ + if (gCodeToEvaluate) { + gEvaluator.contentWindow.location = "javascript: " + + gCodeToEvaluate.replace(/%/g, "%25"); + gCodeToEvaluate = ""; + return; + } + + var resultRange = gEvaluator.contentDocument.createRange(); + resultRange.selectNode(gEvaluator.contentDocument.documentElement); + var result = resultRange.toString(); + if (result) + Services.console.logStringMessage(result); + // or could use appendMessage which doesn't persist +} diff --git a/toolkit/components/console/content/console.xul b/toolkit/components/console/content/console.xul new file mode 100644 index 000000000..0abd0a981 --- /dev/null +++ b/toolkit/components/console/content/console.xul @@ -0,0 +1,101 @@ +<?xml version="1.0"?> <!-- -*- tab-width: 4; indent-tabs-mode: nil -*- --> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://global/skin/console/console.css" type="text/css"?> +<?xml-stylesheet href="chrome://global/content/console.css" type="text/css"?> +<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> + +<!DOCTYPE window [ + <!ENTITY % console SYSTEM "chrome://global/locale/console.dtd"> %console; +]> + +<window id="JSConsoleWindow" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="&errorConsole.title;" + windowtype="global:console" + width="640" height="480" + screenX="10" screenY="10" + persist="screenX screenY width height sizemode" + onclose="return closeWindow(false);"> + + <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> + <script type="application/javascript" src="chrome://global/content/console.js"/> + <script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/> + + <stringbundle id="ConsoleBundle" src="chrome://global/locale/console.properties"/> + + <commandset id="editMenuCommands"/> + + <commandset id="consoleCommands"> + <command id="cmd_close" oncommand="closeWindow(true)"/> + </commandset> + + <keyset id="consoleKeys"> + <key id="key_close" key="&closeCmd.commandkey;" modifiers="accel" + command="cmd_close"/> + <key id="key_close2" keycode="VK_ESCAPE" command="cmd_close"/> + <key id="key_focus1" key="&focus1.commandkey;" modifiers="accel" + oncommand="gTextBoxEval.focus()"/> + <key id="key_focus2" key="&focus2.commandkey;" modifiers="alt" + oncommand="gTextBoxEval.focus()"/> + </keyset> + + <popupset id="ContextMenus"> + <menupopup id="ConsoleContext"> + <menuitem type="radio" id="Console:sortAscend" + label="&sortFirst.label;" accesskey="&sortFirst.accesskey;" + oncommand="changeSortOrder('forward');"/> + <menuitem type="radio" id="Console:sortDescend" + label="&sortLast.label;" accesskey="&sortLast.accesskey;" + oncommand="changeSortOrder('reverse');"/> + <menuseparator/> + <menuitem id="menu_copy_cm" command="cmd_copy" + label="©Cmd.label;" accesskey="©Cmd.accesskey;"/> + </menupopup> + </popupset> + + <toolbox id="console-toolbox"> + <toolbar class="chromeclass-toolbar" id="ToolbarMode"> + <hbox id="viewGroup"> + <toolbarbutton type="radio" group="mode" id="Console:modeAll" + label="&all.label;" accesskey="&all.accesskey;" + oncommand="changeMode('All');"/> + <toolbarbutton type="radio" group="mode" id="Console:modeErrors" + label="&errors.label;" accesskey="&errors.accesskey;" + oncommand="changeMode('Errors');"/> + <toolbarbutton type="radio" group="mode" id="Console:modeWarnings" + label="&warnings.label;" accesskey="&warnings.accesskey;" + oncommand="changeMode('Warnings');"/> + <toolbarbutton type="radio" group="mode" id="Console:modeMessages" + label="&messages.label;" accesskey="&messages.accesskey;" + oncommand="changeMode('Messages');"/> + </hbox> + <toolbarseparator/> + <toolbarbutton id="Console:clear" oncommand="clearConsole();" + label="&clear.label;" accesskey="&clear.accesskey;"/> + </toolbar> + + <toolbar class="chromeclass-toolbar" id="ToolbarEval" align="center" nowindowdrag="true"> + <label value="&codeEval.label;" accesskey="&codeEval.accesskey;" control="TextboxEval"/> + <textbox id="TextboxEval" class="toolbar" value="" onkeypress="onEvalKeyPress(event)" flex="1"/> + <toolbarbutton id="ButtonEval" label="&evaluate.label;" + accesskey="&evaluate.accesskey;" oncommand="evaluateTypein()"/> + </toolbar> + </toolbox> + + <vbox id="ConsoleBox" class="console-box" flex="1" context="ConsoleContext" persist="sortOrder"/> + + <iframe name="Evaluator" id="Evaluator" collapsed="true"/> + + <statusbar> + <statusbarpanel flex="1" pack="start"> + <label value="&filter2.label;" control="Filter"/> + <textbox accesskey="&filter2.accesskey;" type="search" + id="Filter" oncommand="changeFilter();"/> + </statusbarpanel> + </statusbar> +</window> diff --git a/toolkit/components/console/content/consoleBindings.xml b/toolkit/components/console/content/consoleBindings.xml new file mode 100644 index 000000000..1b00326bf --- /dev/null +++ b/toolkit/components/console/content/consoleBindings.xml @@ -0,0 +1,547 @@ +<?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> diff --git a/toolkit/components/console/jar.mn b/toolkit/components/console/jar.mn new file mode 100644 index 000000000..26e54c6fb --- /dev/null +++ b/toolkit/components/console/jar.mn @@ -0,0 +1,9 @@ +# 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/. + +toolkit.jar: + content/global/console.js (content/console.js) + content/global/console.xul (content/console.xul) + content/global/console.css (content/console.css) + content/global/consoleBindings.xml (content/consoleBindings.xml) diff --git a/toolkit/components/console/jsconsole-clhandler.js b/toolkit/components/console/jsconsole-clhandler.js new file mode 100644 index 000000000..7e5d0ea51 --- /dev/null +++ b/toolkit/components/console/jsconsole-clhandler.js @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ +/* vim:sw=4:sr:sta:et:sts: */ + +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function jsConsoleHandler() {} +jsConsoleHandler.prototype = { + handle: function clh_handle(cmdLine) { + if (!cmdLine.handleFlag("jsconsole", false)) + return; + + var wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); + var console = wm.getMostRecentWindow("global:console"); + if (!console) { + var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher); + wwatch.openWindow(null, "chrome://global/content/console.xul", "_blank", + "chrome,dialog=no,all", cmdLine); + } else { + console.focus(); // the Error console was already open + } + + if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) + cmdLine.preventDefault = true; + }, + + helpInfo : " -jsconsole Open the Error console.\n", + + classID: Components.ID("{2cd0c310-e127-44d0-88fc-4435c9ab4d4b}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([jsConsoleHandler]); diff --git a/toolkit/components/console/jsconsole-clhandler.manifest b/toolkit/components/console/jsconsole-clhandler.manifest new file mode 100644 index 000000000..8e8c9b8bb --- /dev/null +++ b/toolkit/components/console/jsconsole-clhandler.manifest @@ -0,0 +1,3 @@ +component {2cd0c310-e127-44d0-88fc-4435c9ab4d4b} jsconsole-clhandler.js +contract @mozilla.org/toolkit/console-clh;1 {2cd0c310-e127-44d0-88fc-4435c9ab4d4b} +category command-line-handler b-jsconsole @mozilla.org/toolkit/console-clh;1 diff --git a/toolkit/components/console/moz.build b/toolkit/components/console/moz.build new file mode 100644 index 000000000..d730b9aa3 --- /dev/null +++ b/toolkit/components/console/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini'] + +EXTRA_COMPONENTS += [ + 'jsconsole-clhandler.js', + 'jsconsole-clhandler.manifest', +] + +JAR_MANIFESTS += ['jar.mn'] diff --git a/toolkit/components/console/tests/chrome.ini b/toolkit/components/console/tests/chrome.ini new file mode 100644 index 000000000..0480c3533 --- /dev/null +++ b/toolkit/components/console/tests/chrome.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[test_hugeURIs.xul] diff --git a/toolkit/components/console/tests/test_hugeURIs.xul b/toolkit/components/console/tests/test_hugeURIs.xul new file mode 100644 index 000000000..87a571e8d --- /dev/null +++ b/toolkit/components/console/tests/test_hugeURIs.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=796179 +--> +<window title="Mozilla Bug 796179" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="RunTest();"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- Detect severe performance and memory issues when large amounts of errors + are reported from CSS embedded in a file with a long data URI. Addressed + by 786108 for issues internal to the style system and by 796179 for issues + related to the error console. This error console test should finish quickly + with those patches and run for a very long time or OOM otherwise. --> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=796179" + target="_blank">Mozilla Bug 796179</a> + <div id="badSVG" style="max-width: 1; max-height: 1; overflow: hidden"></div> + </body> + + <!-- display the error console so we can test its reaction to the test --> + <iframe id="errorConsoleFrame" height="400" src="chrome://global/content/console.xul"></iframe> + + <!-- test code --> + <script type="application/javascript"> + <![CDATA[ + function RunTest() + { + // Create the bad SVG and add it to the document. + var img = new Array; + img.push('<img src="data:image/svg+xml,'); + img.push(encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="300px">')); + + for (var i = 0 ; i < 10000 ; i++) + img.push(encodeURIComponent('<circle cx="0" cy="0" r="1" style="xxx-invalid-property: 0;"/>')); + + img.push(encodeURIComponent('</svg>')); + img.push('" />'); + + document.getElementById('badSVG').innerHTML = img.join(''); + + // We yield control of the thread, allowing the error console to render. + // If we get control back without timing out or OOMing then the test passed. + SimpleTest.waitForExplicitFinish(); + SimpleTest.executeSoon(function() { + // Clean up. + var elem = document.getElementById('errorConsoleFrame'); + elem.parentNode.removeChild(elem); + elem = document.getElementById('badSVG'); + elem.parentNode.removeChild(elem); + elem = null; + + // Finish the test with a pass. + ok(true, 'Error console rendered OK.'); + SimpleTest.finish(); + }, 0); + } + ]]> + </script> +</window> |