Bug 585956 - Implement console.trace() in web console; f=ddahl r=rcampbell,jonas

This commit is contained in:
Mihai Sucan 2011-04-11 20:48:15 +03:00
parent 967ea20a1d
commit 5128602d66
10 changed files with 274 additions and 9 deletions

View File

@ -21,6 +21,7 @@
* David Dahl <ddahl@mozilla.com> (Original Author)
* Ryan Flint <rflint@mozilla.com>
* Rob Campbell <rcampbell@mozilla.com>
* Mihai Sucan <mihai.sucan@gmail.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
@ -79,12 +80,16 @@ ConsoleAPI.prototype = {
debug: function CA_debug() {
self.notifyObservers(id, "log", arguments);
},
trace: function CA_trace() {
self.notifyObservers(id, "trace", self.getStackTrace());
},
__exposedProps__: {
log: "r",
info: "r",
warn: "r",
error: "r",
debug: "r",
trace: "r",
}
};
@ -100,6 +105,7 @@ ConsoleAPI.prototype = {
warn: bind.call(x.warn, x),\
error: bind.call(x.error, x),\
debug: bind.call(x.debug, x),\
trace: bind.call(x.trace, x),\
__noSuchMethod__: function() {}\
};\
Object.defineProperty(obj, '__mozillaConsole__', { value: true });\
@ -126,7 +132,32 @@ ConsoleAPI.prototype = {
Services.obs.notifyObservers(consoleEvent,
"console-api-log-event", aID);
}
},
/**
* Build the stacktrace array for the console.trace() call.
*
* @return array
* Each element is a stack frame that holds the following properties:
* filename, lineNumber, functionName and language.
**/
getStackTrace: function CA_getStackTrace() {
let stack = [];
let frame = Components.stack.caller;
while (frame = frame.caller) {
if (frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT ||
frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT2) {
stack.push({
filename: frame.filename,
lineNumber: frame.lineNumber,
functionName: frame.name,
language: frame.language,
});
}
}
return stack;
},
};
let NSGetFactory = XPCOMUtils.generateNSGetFactory([ConsoleAPI]);

View File

@ -21,6 +21,7 @@
* Contributor(s):
* David Dahl <ddahl@mozilla.com>
* Rob Campbell <rcampbell@mozilla.com>
* Mihai Sucan <mihai.sucan@gmail.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
@ -73,15 +74,39 @@ function testConsoleData(aMessageObject) {
is(aMessageObject.level, gLevel, "expected level received");
ok(aMessageObject.arguments, "we have arguments");
is(aMessageObject.arguments.length, gArgs.length, "arguments.length matches");
gArgs.forEach(function (a, i) {
is(aMessageObject.arguments[i], a, "correct arg " + i);
});
if (aMessageObject.level == "error") {
if (gLevel == "trace") {
is(aMessageObject.arguments.toSource(), gArgs.toSource(),
"stack trace is correct");
// Test finished
ConsoleObserver.destroy();
finish();
}
else {
gArgs.forEach(function (a, i) {
is(aMessageObject.arguments[i], a, "correct arg " + i);
});
}
if (aMessageObject.level == "error") {
// Now test console.trace()
startTraceTest();
}
}
function startTraceTest() {
gLevel = "trace";
gArgs = [
{filename: TEST_URI, lineNumber: 6, functionName: null, language: 2},
{filename: TEST_URI, lineNumber: 11, functionName: "foobar585956b", language: 2},
{filename: TEST_URI, lineNumber: 15, functionName: "foobar585956a", language: 2},
{filename: TEST_URI, lineNumber: 1, functionName: "onclick", language: 2}
];
let button = gWindow.document.getElementById("test-trace");
ok(button, "found #test-trace button");
EventUtils.synthesizeMouse(button, 2, 2, {}, gWindow);
}
var gLevel, gArgs;
@ -114,6 +139,7 @@ function consoleAPISanityTest() {
ok(win.console.info, "console.info is here");
ok(win.console.warn, "console.warn is here");
ok(win.console.error, "console.error is here");
ok(win.console.trace, "console.trace is here");
}
var ConsoleObserver = {

View File

@ -2,6 +2,19 @@
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
<title>Console API test page</title>
<script type="text/javascript">
window.foobar585956c = function(a) {
console.trace();
return a+"c";
};
function foobar585956b(a) {
return foobar585956c(a+"b");
}
function foobar585956a(omg) {
return foobar585956b(omg + "a");
}
function test() {
var str = "Test Message."
console.foobar(str); // if this throws, we don't execute following funcs
@ -15,5 +28,6 @@
<body>
<h1>Console API Test Page</h1>
<button onclick="test();">Log stuff</button>
<button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
</body>
</html>

View File

@ -26,6 +26,7 @@ function doTest() {
"warn": "function",
"error": "function",
"debug": "function",
"trace": "function",
"__noSuchMethod__": "function"
};

View File

@ -145,6 +145,7 @@ const LEVELS = {
warn: SEVERITY_WARNING,
info: SEVERITY_INFO,
log: SEVERITY_LOG,
trace: SEVERITY_LOG,
};
// The lowest HTTP response code (inclusive) that is considered an error.
@ -1981,12 +1982,89 @@ HUD_SERVICE.prototype =
function formatResult(x) {
return (typeof(x) == "string") ? x : hud.jsterm.formatResult(x);
}
let mappedArguments = Array.map(aArguments, formatResult);
let joinedArguments = Array.join(mappedArguments, " ");
let body = null;
let clipboardText = null;
let sourceURL = null;
let sourceLine = 0;
switch (aLevel) {
case "log":
case "info":
case "warn":
case "error":
case "debug":
let mappedArguments = Array.map(aArguments, formatResult);
body = Array.join(mappedArguments, " ");
break;
case "trace":
let filename = ConsoleUtils.abbreviateSourceURL(aArguments[0].filename);
let functionName = aArguments[0].functionName ||
this.getStr("stacktrace.anonymousFunction");
let lineNumber = aArguments[0].lineNumber;
body = this.getFormatStr("stacktrace.outputMessage",
[filename, functionName, lineNumber]);
sourceURL = aArguments[0].filename;
sourceLine = aArguments[0].lineNumber;
clipboardText = "";
aArguments.forEach(function(aFrame) {
clipboardText += aFrame.filename + " :: " +
aFrame.functionName + " :: " +
aFrame.lineNumber + "\n";
});
clipboardText = clipboardText.trimRight();
break;
default:
Cu.reportError("Unknown Console API log level: " + aLevel);
return;
}
let node = ConsoleUtils.createMessageNode(hud.outputNode.ownerDocument,
CATEGORY_WEBDEV,
LEVELS[aLevel],
joinedArguments);
body,
sourceURL,
sourceLine,
clipboardText);
// Make the node bring up the property panel, to allow the user to inspect
// the stack trace.
if (aLevel == "trace") {
node._stacktrace = aArguments;
let linkNode = node.querySelector(".webconsole-msg-body");
linkNode.classList.add("hud-clickable");
linkNode.setAttribute("aria-haspopup", "true");
node.addEventListener("mousedown", function(aEvent) {
this._startX = aEvent.clientX;
this._startY = aEvent.clientY;
}, false);
node.addEventListener("click", function(aEvent) {
if (aEvent.detail != 1 || aEvent.button != 0 ||
(this._startX != aEvent.clientX &&
this._startY != aEvent.clientY)) {
return;
}
if (!this._panelOpen) {
let propPanel = hud.jsterm.openPropertyPanel(null,
node._stacktrace,
this);
propPanel.panel.setAttribute("hudId", aHUDId);
this._panelOpen = true;
}
}, false);
}
ConsoleUtils.outputMessageNode(node, aHUDId);
},

View File

@ -131,6 +131,7 @@ _BROWSER_TEST_FILES = \
browser_webconsole_bug_632347_iterators_generators.js \
browser_webconsole_bug_642108_refForOutputNode.js \
browser_webconsole_bug_642108_pruneTest.js \
browser_webconsole_bug_585956_console_trace.js \
head.js \
$(NULL)
@ -199,6 +200,7 @@ _BROWSER_TEST_PAGES = \
test-bug-630733-response-redirect-headers.sjs \
test-bug-621644-jsterm-dollar.html \
test-bug-632347-iterators-generators.html \
test-bug-585956-console-trace.html \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)

View File

@ -0,0 +1,76 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Web Console test suite.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mihai Sucan <mihai.sucan@gmail.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"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-bug-585956-console-trace.html";
function test() {
addTab(TEST_URI);
browser.addEventListener("load", tabLoaded, true);
}
function tabLoaded() {
browser.removeEventListener("load", tabLoaded, true);
openConsole();
browser.addEventListener("load", tabReloaded, true);
content.location.reload();
}
function tabReloaded() {
browser.removeEventListener("load", tabReloaded, true);
// The expected stack trace object.
let stacktrace = [
{ filename: TEST_URI, lineNumber: 9, functionName: null, language: 2 },
{ filename: TEST_URI, lineNumber: 14, functionName: "foobar585956b", language: 2 },
{ filename: TEST_URI, lineNumber: 18, functionName: "foobar585956a", language: 2 },
{ filename: TEST_URI, lineNumber: 21, functionName: null, language: 2 }
];
let hudId = HUDService.getHudIdByWindow(content);
let HUD = HUDService.hudReferences[hudId];
let node = HUD.outputNode.querySelector(".hud-log");
ok(node, "found trace log node");
ok(node._stacktrace, "found stacktrace object");
is(node._stacktrace.toSource(), stacktrace.toSource(), "stacktrace is correct");
isnot(node.textContent.indexOf("bug-585956"), -1, "found file name");
finishTest();
}

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Web Console test for bug 585956 - console.trace()</title>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<script type="application/javascript">
window.foobar585956c = function(a) {
console.trace();
return a+"c";
};
function foobar585956b(a) {
return foobar585956c(a+"b");
}
function foobar585956a(omg) {
return foobar585956b(omg + "a");
}
foobar585956a("omg");
</script>
</head>
<body>
<p>Web Console test for bug 585956 - console.trace().</p>
</body>
</html>

View File

@ -11,7 +11,6 @@
console.clear()
console.dir()
console.dirxml()
console.trace()
console.group()
console.groupCollapsed()
console.groupEnd()

View File

@ -114,3 +114,14 @@ NetworkPanel.imageSizeDeltaDurationMS=%Sx%Spx, Δ%Sms
# o music/crescendo
NetworkPanel.responseBodyUnableToDisplay.content=Unable to display responses of type "%S"
ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
# LOCALIZATION NOTE (stacktrace.anonymousFunction):
# This string is used to display JavaScript functions that have no given name -
# they are said to be anonymous. See stacktrace.outputMessage.
stacktrace.anonymousFunction=<anonymous>
# LOCALIZATION NOTE (stacktrace.outputMessage):
# This string is used in the Web Console output to identify a web developer call
# to console.trace(). The stack trace of JavaScript function calls is displayed.
# In this minimal message we only show the last call.
stacktrace.outputMessage=Stack trace from %S, function %S, line %S.