Bug 611795 - Repeated messages in the Web Console should be collapsed into one; f=mihai.sucan r=sdwilsh,dao a=beltzner

This commit is contained in:
Rob Campbell 2011-02-25 18:12:42 -04:00
parent a9faf09c6b
commit 1863b39a87
7 changed files with 291 additions and 15 deletions

View File

@ -1201,11 +1201,17 @@ function pruneConsoleOutputIfNecessary(aConsoleNode)
let scrollBox = aConsoleNode.scrollBoxObject.element;
let oldScrollHeight = scrollBox.scrollHeight;
let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(aConsoleNode);
let hudRef = HUDService.getHudReferenceForOutputNode(aConsoleNode);
// Prune the nodes.
let messageNodes = aConsoleNode.querySelectorAll(".hud-msg-node");
let removeNodes = messageNodes.length - logLimit;
for (let i = 0; i < removeNodes; i++) {
if (messageNodes[i].classList.contains("webconsole-msg-cssparser")) {
let desc = messageNodes[i].childNodes[2].textContent;
let location = messageNodes[i].childNodes[4].getAttribute("title");
delete hudRef.cssNodes[desc + location];
}
messageNodes[i].parentNode.removeChild(messageNodes[i]);
}
@ -1458,6 +1464,12 @@ HUD_SERVICE.prototype =
aHUD = this.getOutputNodeById(aHUD);
}
let hudRef = HUDService.getHudReferenceForOutputNode(aHUD);
if (hudRef) {
hudRef.cssNodes = {};
}
var outputNode = aHUD.querySelector(".hud-output-node");
while (outputNode.firstChild) {
@ -1734,6 +1746,9 @@ HUD_SERVICE.prototype =
parent.removeChild(outputNode);
// remove the HeadsUpDisplay object from memory
if ("cssNodes" in this.hudReferences[id]) {
delete this.hudReferences[id].cssNodes;
}
delete this.hudReferences[id];
// remove the related storage object
this.storage.removeDisplay(id);
@ -1827,6 +1842,39 @@ HUD_SERVICE.prototype =
return this.getHudIdByWindowId(windowId);
},
/**
* Returns the hudReference for a given output node.
*
* @param nsIDOMNode aNode
* an output node (as returned by getOutputNodeById()).
* @returns a HUD | null
*/
getHudReferenceForOutputNode: function HS_getHudReferenceForOutputNode(aNode)
{
let node = aNode;
while (!node.classList.contains("hudbox-animated")) {
if (node.parent) {
node = node.parent;
}
else {
return null;
}
}
let id = node.id;
return id in this.hudReferences ? this.hudReferences[id] : null;
},
/**
* Returns the hudReference for a given id.
*
* @param string aId
* @returns Object
*/
getHudReferenceById: function HS_getHudReferenceById(aId)
{
return aId in this.hudReferences ? this.hudReferences[aId] : null;
},
/**
* Gets the Web Console DOM node, the .hud-box.
*
@ -1872,7 +1920,7 @@ HUD_SERVICE.prototype =
},
/**
* get the current filter string for the HeadsUpDisplay
* Get the current filter string for the HeadsUpDisplay
*
* @param string aHUDId
* @returns string
@ -2987,6 +3035,9 @@ function HeadsUpDisplay(aConfig)
catch (ex) {
Cu.reportError(ex);
}
// A cache for tracking repeated CSS Nodes.
this.cssNodes = {};
}
HeadsUpDisplay.prototype = {
@ -4329,6 +4380,11 @@ JSTerm.prototype = {
clearOutput: function JST_clearOutput()
{
let outputNode = this.outputNode;
let hudRef = HUDService.getHudReferenceForOutputNode(outputNode);
if (hudRef) {
hudRef.cssNodes = {};
}
while (outputNode.firstChild) {
outputNode.removeChild(outputNode.firstChild);
@ -4943,6 +4999,10 @@ ConsoleUtils = {
bodyNode.appendChild(aBody);
let repeatNode = aDocument.createElementNS(XUL_NS, "xul:label");
repeatNode.setAttribute("value", "1");
repeatNode.classList.add("webconsole-msg-repeat");
// Create the timestamp.
let timestampNode = aDocument.createElementNS(XUL_NS, "xul:label");
timestampNode.classList.add("webconsole-timestamp");
@ -4966,11 +5026,12 @@ ConsoleUtils = {
node.timestamp = timestamp;
ConsoleUtils.setMessageType(node, aCategory, aSeverity);
node.appendChild(timestampNode);
node.appendChild(iconContainer);
node.appendChild(bodyNode);
node.appendChild(timestampNode); // childNode[0]
node.appendChild(iconContainer); // childNode[1]
node.appendChild(bodyNode); // childNode[2]
node.appendChild(repeatNode); // childNode[3]
if (locationNode) {
node.appendChild(locationNode);
node.appendChild(locationNode); // childNode[4]
}
return node;
@ -5065,7 +5126,7 @@ ConsoleUtils = {
* @param string aHUDId
* The ID of the HUD which this node is to be inserted into.
*/
filterMessageNode: function(aNode, aHUDId) {
filterMessageNode: function ConsoleUtils_filterMessageNode(aNode, aHUDId) {
// Filter by the message type.
let prefKey = MESSAGE_PREFERENCE_KEYS[aNode.category][aNode.severity];
if (prefKey && !HUDService.getFilterState(aHUDId, prefKey)) {
@ -5084,7 +5145,95 @@ ConsoleUtils = {
},
/**
* Filters a node appropriately, then sends it to the output, regrouping and
* Merge the attributes of the two nodes that are about to be filtered.
* Increment the number of repeats of aOriginal.
*
* @param nsIDOMNode aOriginal
* The Original Node. The one being merged into.
* @param nsIDOMNode aFiltered
* The node being filtered out because it is repeated.
*/
mergeFilteredMessageNode:
function ConsoleUtils_mergeFilteredMessageNode(aOriginal, aFiltered) {
// childNodes[3] is the node containing the number of repetitions of a node.
let repeatNode = aOriginal.childNodes[3];
if (!repeatNode) {
return aOriginal; // no repeat node, return early.
}
let occurrences = parseInt(repeatNode.getAttribute("value")) + 1;
repeatNode.setAttribute("value", occurrences);
},
/**
* Filter the css node from the output node if it is a repeat. CSS messages
* are merged with previous messages if they occurred in the past.
*
* @param nsIDOMNode aNode
* The message node to be filtered or not.
* @param nsIDOMNode aOutput
* The outputNode of the HUD.
* @returns boolean
* true if the message is filtered, false otherwise.
*/
filterRepeatedCSS:
function ConsoleUtils_filterRepeatedCSS(aNode, aOutput, aHUDId) {
let hud = HUDService.getHudReferenceById(aHUDId);
// childNodes[2] is the description node containing the text of the message.
let description = aNode.childNodes[2].textContent;
let location;
// childNodes[4] represents the location (source URL) of the error message.
// The full source URL is stored in the title attribute.
if (aNode.childNodes[4]) {
// browser_webconsole_bug_595934_message_categories.js
location = aNode.childNodes[4].getAttribute("title");
}
else {
location = "";
}
let dupe = hud.cssNodes[description + location];
if (!dupe) {
// no matching nodes
hud.cssNodes[description + location] = aNode;
return false;
}
this.mergeFilteredMessageNode(dupe, aNode);
return true;
},
/**
* Filter the console node from the output node if it is a repeat. Console
* messages are filtered from the output if and only if they match the
* immediately preceding message. The output node's last occurrence should
* have its timestamp updated.
*
* @param nsIDOMNode aNode
* The message node to be filtered or not.
* @param nsIDOMNode aOutput
* The outputNode of the HUD.
* @return boolean
* true if the message is filtered, false otherwise.
*/
filterRepeatedConsole:
function ConsoleUtils_filterRepeatedConsole(aNode, aOutput) {
let lastMessage = aOutput.lastChild;
// childNodes[2] is the xul:description element
if (lastMessage &&
aNode.childNodes[2].textContent ==
lastMessage.childNodes[2].textContent) {
return true;
}
return false;
},
/** * Filters a node appropriately, then sends it to the output, regrouping and
* pruning output as necessary.
*
* @param nsIDOMNode aNode
@ -5099,7 +5248,22 @@ ConsoleUtils = {
let scrolledToBottom = ConsoleUtils.isOutputScrolledToBottom(outputNode);
outputNode.appendChild(aNode);
let isRepeated = false;
if (aNode.classList.contains("webconsole-msg-cssparser")) {
isRepeated = this.filterRepeatedCSS(aNode, outputNode, aHUDId);
}
if (!isRepeated &&
(aNode.classList.contains("webconsole-msg-console") ||
aNode.classList.contains("webconsole-msg-exception") ||
aNode.classList.contains("webconsole-msg-error"))) {
isRepeated = this.filterRepeatedConsole(aNode, outputNode, aHUDId);
}
if (!isRepeated) {
outputNode.appendChild(aNode);
}
HUDService.regroupOutput(outputNode);
if (pruneConsoleOutputIfNecessary(outputNode) == 0) {
@ -5116,7 +5280,7 @@ ConsoleUtils = {
// Scroll to the new node if it is not filtered, and if the output node is
// scrolled at the bottom or if the new node is a jsterm input/output
// message.
if (!isFiltered && (scrolledToBottom || isInputOutput)) {
if (!isFiltered && !isRepeated && (scrolledToBottom || isInputOutput)) {
ConsoleUtils.scrollToVisible(aNode);
}
},

View File

@ -32,8 +32,9 @@ function testLineLimit() {
prefBranch.setIntPref("loglimit", 20);
for (let i = 0; i < 20; i++) {
console.log("foo");
console.log("foo #" + i); // must change message to prevent repeats
}
is(countMessageNodes(), 20, "there are 20 message nodes in the output " +
"when the log limit is set to 20");
@ -43,8 +44,9 @@ function testLineLimit() {
prefBranch.setIntPref("loglimit", 30);
for (let i = 0; i < 20; i++) {
console.log("boo");
console.log("boo #" + i); // must change message to prevent repeats
}
is(countMessageNodes(), 30, "there are 30 message nodes in the output " +
"when the log limit is set to 30");

View File

@ -0,0 +1,50 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = 'data:text/html,<div style="-moz-opacity:0;">test repeated' +
' css warnings</div><p style="-moz-opacity:0">hi</p>';
function onContentLoaded()
{
browser.removeEventListener("load", arguments.callee, true);
hudId = HUDService.displaysIndex()[0];
HUD = HUDService.hudReferences[hudId].HUDBox;
jsterm = HUDService.hudReferences[hudId].jsterm;
let filterBox = HUD.querySelector(".hud-filter-box");
let outputNode = HUD.querySelector(".hud-output-node");
let msg = "The unknown CSS property warning is displayed only once";
let node = outputNode.firstChild;
is (node.childNodes[2].textContent, "Unknown property '-moz-opacity'. Declaration dropped.", "correct node")
is(node.childNodes[3].getAttribute("value"), 2, msg);
jsterm.clearOutput();
jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('hi');");
jsterm.execute();
msg = "The console output is repeated 10 times";
let node = outputNode.querySelector(".webconsole-msg-console");
is(node.childNodes[3].getAttribute("value"), 10, msg);
finishTest();
}
/**
* Unit test for bug 611795:
* Repeated CSS messages get collapsed into one.
*/
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function() {
browser.removeEventListener("load", arguments.callee, true);
openConsole();
browser.addEventListener("load", onContentLoaded, true);
content.location.reload();
}, true);
}

View File

@ -84,14 +84,14 @@ function testConsoleLoggingAPI(aMethod) {
setStringFilter(hudId, "");
HUDService.setFilterState(hudId, aMethod, false);
console[aMethod]("foo-bar-baz");
nodes = outputNode.querySelectorAll("label");
nodes = outputNode.querySelectorAll("description");
is(nodes.length, 1, aMethod + " logging turned off, 1 message hidden");
HUDService.clearDisplay(hudId);
HUDService.setFilterState(hudId, aMethod, true);
console[aMethod]("foo-bar-baz");
nodes = outputNode.querySelectorAll("label");
nodes = outputNode.querySelectorAll("description");
is(nodes.length, 1, aMethod + " logging turned on, 1 message shown");

View File

@ -22,6 +22,7 @@
* David Dahl <ddahl@mozilla.com>
* Patrick Walton <pcwalton@mozilla.com>
* Mihai Șucan <mihai.sucan@gmail.com>
* Rob Campbell <rcampbell@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -96,6 +97,25 @@
margin: 0 6px;
}
/* Repeated messages */
.webconsole-msg-repeat {
margin: 2px 0;
padding-left: 4px;
padding-right: 4px;
width: 1em;
color: white;
background-color: red;
border-radius: 40px;
font: message-box;
font-size: 10px;
font-weight: 600;
}
/* TODO move this and other functional rules to content - bug 635359 */
.webconsole-msg-repeat[value="1"] {
display: none;
}
.webconsole-location {
margin-top: 0;
margin-bottom: 0;

View File

@ -21,7 +21,8 @@
* Contributor(s):
* David Dahl <ddahl@mozilla.com>
* Patrick Walton <pcwalton@mozilla.com>
* Rob Campbell <rcampbell@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@ -99,6 +100,25 @@
margin: 0 6px;
}
/* Repeated messages */
.webconsole-msg-repeat {
margin: 2px 0;
padding-left: 4px;
padding-right: 4px;
width: 1em;
color: white;
background-color: red;
border-radius: 40px;
font: message-box;
font-size: 10px;
font-weight: 600;
}
/* TODO move this and other functional rules to content - bug 635359 */
.webconsole-msg-repeat[value="1"] {
display: none;
}
.webconsole-location {
margin-top: 0;
margin-bottom: 0;

View File

@ -21,7 +21,8 @@
* Contributor(s):
* David Dahl <ddahl@mozilla.com>
* Patrick Walton <pcwalton@mozilla.com>
* Rob Campbell <rcampbell@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@ -95,6 +96,25 @@
margin: 0 6px;
}
/* Repeated messages */
.webconsole-msg-repeat {
margin: 2px 0;
padding-left: 4px;
padding-right: 4px;
width: 1em;
color: white;
background-color: red;
border-radius: 40px;
font: message-box;
font-size: 10px;
font-weight: 600;
}
/* TODO move this and other functional rules to content - bug 635359 */
.webconsole-msg-repeat[value="1"] {
display: none;
}
.webconsole-location {
margin-top: 0;
margin-bottom: 0;