summaryrefslogtreecommitdiffstats
path: root/toolkit/components/console
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-02 07:11:37 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-02 07:11:37 -0500
commit464e29230eeb8c25dfb064f14239b1288c85332b (patch)
tree08935287f435060c489af48cd4a59d00e76197c6 /toolkit/components/console
parent6d614170cbfa958564eb5f824234ad5a9e484344 (diff)
downloadUXP-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.css74
-rw-r--r--toolkit/components/console/content/console.js111
-rw-r--r--toolkit/components/console/content/console.xul101
-rw-r--r--toolkit/components/console/content/consoleBindings.xml547
-rw-r--r--toolkit/components/console/jar.mn9
-rw-r--r--toolkit/components/console/jsconsole-clhandler.js40
-rw-r--r--toolkit/components/console/jsconsole-clhandler.manifest3
-rw-r--r--toolkit/components/console/moz.build14
-rw-r--r--toolkit/components/console/tests/chrome.ini3
-rw-r--r--toolkit/components/console/tests/test_hugeURIs.xul64
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="&copyCmd.label;" accesskey="&copyCmd.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>