Bug 598357 - use toSource() instead of toString() for some types of output in the Console; f=rcampbell r=gavin.sharp, a=blocking2.0

This commit is contained in:
Mihai Sucan 2010-12-14 10:51:16 -08:00
parent f3a4796451
commit d82da2ea64
6 changed files with 390 additions and 34 deletions

View File

@ -1916,7 +1916,9 @@ HUD_SERVICE.prototype =
let klass = "hud-msg-node hud-" + aLevel;
messageNode.setAttribute("class", klass);
let message = Array.join(aArguments, " ") + "\n";
let mappedArguments = Array.map(aArguments, hud.jsterm.formatResult,
hud.jsterm);
let message = Array.join(mappedArguments, " ") + "\n";
let ts = ConsoleUtils.timestamp();
let timestampedMessage = ConsoleUtils.timestampString(ts) + ": " + message;
messageNode.appendChild(hud.chromeDocument.createTextNode(timestampedMessage));
@ -3804,6 +3806,7 @@ function JSTermHelper(aJSTerm)
*/
aJSTerm.sandbox.clear = function JSTH_clear()
{
aJSTerm.helperEvaluated = true;
aJSTerm.clearOutput();
};
@ -3852,6 +3855,7 @@ function JSTermHelper(aJSTerm)
*/
aJSTerm.sandbox.help = function JSTH_help()
{
aJSTerm.helperEvaluated = true;
aJSTerm._window.open(
"https://developer.mozilla.org/AppLinks/WebConsoleHelp?locale=" +
aJSTerm._window.navigator.language, "help", "");
@ -3866,6 +3870,7 @@ function JSTermHelper(aJSTerm)
*/
aJSTerm.sandbox.inspect = function JSTH_inspect(aObject)
{
aJSTerm.helperEvaluated = true;
aJSTerm.openPropertyPanel(null, unwrap(aObject));
};
@ -3878,6 +3883,7 @@ function JSTermHelper(aJSTerm)
*/
aJSTerm.sandbox.pprint = function JSTH_pprint(aObject)
{
aJSTerm.helperEvaluated = true;
if (aObject === null || aObject === undefined || aObject === true || aObject === false) {
aJSTerm.console.error(HUDService.getStr("helperFuncUnsupportedTypeError"));
return;
@ -3891,6 +3897,19 @@ function JSTermHelper(aJSTerm)
aJSTerm.writeOutput(output.join("\n"));
};
/**
* Print a string to the output, as-is.
*
* @param string aString
* A string you want to output.
* @returns void
*/
aJSTerm.sandbox.print = function JSTH_print(aString)
{
aJSTerm.helperEvaluated = true;
aJSTerm.writeOutput(aString);
};
}
/**
@ -4020,16 +4039,21 @@ JSTerm.prototype = {
this.writeOutput(aExecuteString, true);
try {
var result = this.evalInSandbox(aExecuteString);
this.helperEvaluated = false;
let result = this.evalInSandbox(aExecuteString);
if (result || result === false) {
this.writeOutputJS(aExecuteString, result);
}
else if (result === undefined) {
this.writeOutput("undefined", false);
}
else if (result === null) {
this.writeOutput("null", false);
// Hide undefined results coming from helpers.
let shouldShow = !(result === undefined && this.helperEvaluated);
if (shouldShow) {
let inspectable = this.isResultInspectable(result);
let resultString = this.formatResult(result);
if (inspectable) {
this.writeOutputJS(aExecuteString, result, resultString);
}
else {
this.writeOutput(resultString);
}
}
}
catch (ex) {
@ -4122,10 +4146,12 @@ JSTerm.prototype = {
*
* @param string aEvalString
* String that was evaluated to get the aOutputObject.
* @param object aOutputObject
* Object to be written to the outputNode.
* @param object aResultObject
* The evaluation result object.
* @param object aOutputString
* The output string to be written to the outputNode.
*/
writeOutputJS: function JST_writeOutputJS(aEvalString, aOutputObject)
writeOutputJS: function JST_writeOutputJS(aEvalString, aOutputObject, aOutputString)
{
let lastGroupNode = HUDService.appendGroupIfNecessary(this.outputNode,
Date.now());
@ -4154,10 +4180,7 @@ JSTerm.prototype = {
}
}, false);
// TODO: format the aOutputObject and don't just use the
// aOuputObject.toString() function: [object object] -> Object {prop, ...}
// See bug 586249.
let textNode = this.textFactory(aOutputObject + "\n");
let textNode = this.textFactory(aOutputString + "\n");
node.appendChild(textNode);
lastGroupNode.appendChild(node);
@ -4202,6 +4225,118 @@ JSTerm.prototype = {
pruneConsoleOutputIfNecessary(this.outputNode);
},
/**
* Format the jsterm execution result based on its type.
*
* @param mixed aResult
* The evaluation result object you want displayed.
* @returns string
* The string that can be displayed.
*/
formatResult: function JST_formatResult(aResult)
{
let output = "";
let type = this.getResultType(aResult);
switch (type) {
case "string":
output = this.formatString(aResult);
break;
case "boolean":
case "date":
case "error":
case "number":
case "regexp":
output = aResult.toString();
break;
case "null":
case "undefined":
output = type;
break;
default:
if (aResult.toSource) {
try {
output = aResult.toSource();
} catch (ex) { }
}
if (!output || output == "({})") {
output = aResult.toString();
}
break;
}
return output;
},
/**
* Format a string for output.
*
* @param string aString
* The string you want to display.
* @returns string
* The string that can be displayed.
*/
formatString: function JST_formatString(aString)
{
function isControlCode(c) {
// See http://en.wikipedia.org/wiki/C0_and_C1_control_codes
// C0 is 0x00-0x1F, C1 is 0x80-0x9F (inclusive).
// We also include DEL (U+007F) and NBSP (U+00A0), which are not strictly
// in C1 but border it.
return (c <= 0x1F) || (0x7F <= c && c <= 0xA0);
}
function replaceFn(aMatch, aType, aHex) {
// Leave control codes escaped, but unescape the rest of the characters.
let c = parseInt(aHex, 16);
return isControlCode(c) ? aMatch : String.fromCharCode(c);
}
let output = uneval(aString).replace(/\\(x)([0-9a-fA-F]{2})/g, replaceFn)
.replace(/\\(u)([0-9a-fA-F]{4})/g, replaceFn);
return output;
},
/**
* Determine if the jsterm execution result is inspectable or not.
*
* @param mixed aResult
* The evaluation result object you want to check if it is inspectable.
* @returns boolean
* True if the object is inspectable or false otherwise.
*/
isResultInspectable: function JST_isResultInspectable(aResult)
{
let isEnumerable = false;
for (let p in aResult) {
isEnumerable = true;
break;
}
return isEnumerable && typeof(aResult) != "string";
},
/**
* Determine the type of the jsterm execution result.
*
* @param mixed aResult
* The evaluation result object you want to check.
* @returns string
* Constructor name or type: string, number, boolean, regexp, date,
* function, object, null, undefined...
*/
getResultType: function JST_getResultType(aResult)
{
let type = aResult === null ? "null" : typeof aResult;
if (type == "object" && aResult.constructor && aResult.constructor.name) {
type = aResult.constructor.name;
}
return type.toLowerCase();
},
clearOutput: function JST_clearOutput()
{
let outputNode = this.outputNode;

View File

@ -110,6 +110,7 @@ _BROWSER_TEST_FILES = \
browser_webconsole_bug_587615_lastTimestamp.js \
browser_webconsole_bug_597460_filter_scroll.js \
browser_webconsole_console_extras.js \
browser_webconsole_bug_598357_jsterm_output.js \
head.js \
$(NULL)

View File

@ -0,0 +1,218 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* Contributor(s):
* Mihai Șucan <mihai.sucan@gmail.com>
*
* ***** END LICENSE BLOCK ***** */
const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-console.html";
let testEnded = false;
let pos = -1;
let dateNow = Date.now();
let inputValues = [
// [showsPropertyPanel?, input value, expected output format,
// print() output, optional console API test]
// 0
[false, "'hello \\nfrom \\rthe \\\"string world!'",
'"hello \\nfrom \\rthe \\"string world!"',
"hello \nfrom \rthe \"string world!"],
// 1
[false, "'\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165'",
"\"\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165\"",
"\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165"],
// 2
[false, "window.location.href", '"' + TEST_URI + '"', TEST_URI],
// 3
[false, "0", "0"],
// 4
[false, "'0'", '"0"', "0"],
// 5
[false, "42", "42"],
// 6
[false, "'42'", '"42"', "42"],
// 7
[false, "/foobar/", "/foobar/"],
// 8
[false, "null", "null"],
// 9
[false, "undefined", "undefined"],
// 10
[false, "true", "true"],
// 11
[false, "document.getElementById", "function getElementById() {[native code]}",
"function getElementById() {\n [native code]\n}",
"document.wrappedJSObject.getElementById"],
// 12
[false, "function() { return 42; }", "function () {return 42;}",
"function () {\n return 42;\n}"],
// 13
[false, "new Date(" + dateNow + ")", (new Date(dateNow)).toString()],
// 14
[true, "document.body", "[object HTMLBodyElement", "[object HTMLBodyElement",
"document.wrappedJSObject.body"],
// 15
[true, "window.location", TEST_URI],
// 16
[true, "[1,2,3,'a','b','c','4','5']", '[1, 2, 3, "a", "b", "c", "4", "5"]',
'1,2,3,a,b,c,4,5'],
// 17
[true, "({a:'b', c:'d', e:1, f:'2'})", '({a:"b", c:"d", e:1, f:"2"})',
"[object Object"],
];
let eventHandlers = [];
let popupShown = [];
function tabLoad(aEvent) {
browser.removeEventListener(aEvent.type, arguments.callee, true);
waitForFocus(function () {
openConsole();
let hudId = HUDService.getHudIdByWindow(content);
HUD = HUDService.hudReferences[hudId];
executeSoon(testNext);
}, content);
}
function testNext() {
let cpos = ++pos;
if (cpos == inputValues.length) {
if (popupShown.length == inputValues.length) {
executeSoon(testEnd);
}
return;
}
let showsPropertyPanel = inputValues[cpos][0];
let inputValue = inputValues[cpos][1];
let expectedOutput = inputValues[cpos][2];
let printOutput = inputValues[cpos].length >= 4 ?
inputValues[cpos][3] : expectedOutput;
let consoleTest = inputValues[cpos][4] || inputValue;
HUD.jsterm.clearOutput();
// Ugly but it does the job.
with (content) {
eval("HUD.console.log(" + consoleTest + ")");
}
let outputItem = HUD.outputNode.
querySelector(".hud-log:last-child");
ok(outputItem,
"found the window.console output line for inputValues[" + cpos + "]");
ok(outputItem.textContent.indexOf(expectedOutput) > -1,
"console API output is correct for inputValues[" + cpos + "]");
HUD.jsterm.clearOutput();
HUD.jsterm.setInputValue("print(" + inputValue + ")");
HUD.jsterm.execute();
outputItem = HUD.outputNode.querySelector(".jsterm-output-line:last-child");
ok(outputItem,
"found the jsterm print() output line for inputValues[" + cpos + "]");
ok(outputItem.textContent.indexOf(printOutput) > -1,
"jsterm print() output is correct for inputValues[" + cpos + "]");
let eventHandlerID = eventHandlers.length + 1;
let propertyPanelShown = function(aEvent) {
let label = aEvent.target.getAttribute("label");
if (!label || label.indexOf(inputValue) == -1) {
return;
}
document.removeEventListener(aEvent.type, arguments.callee, false);
eventHandlers[eventHandlerID] = null;
ok(showsPropertyPanel,
"the property panel shown for inputValues[" + cpos + "]");
aEvent.target.hidePopup();
popupShown[cpos] = true;
if (popupShown.length == inputValues.length) {
executeSoon(testEnd);
}
};
document.addEventListener("popupshown", propertyPanelShown, false);
eventHandlers.push(propertyPanelShown);
HUD.jsterm.clearOutput();
HUD.jsterm.setInputValue(inputValue);
HUD.jsterm.execute();
outputItem = HUD.outputNode.querySelector(".jsterm-output-line:last-child");
ok(outputItem, "found the jsterm output line for inputValues[" + cpos + "]");
ok(outputItem.textContent.indexOf(expectedOutput) > -1,
"jsterm output is correct for inputValues[" + cpos + "]");
outputItem.addEventListener("click", function(aEvent) {
this.removeEventListener(aEvent.type, arguments.callee, false);
executeSoon(testNext);
}, false);
// Send the mousedown, mouseup and click events to check if the property
// panel opens.
EventUtils.synthesizeMouse(outputItem, 1, 1, {}, window);
}
function testEnd() {
if (testEnded) {
return;
}
testEnded = true;
for (let i = 0; i < eventHandlers.length; i++) {
if (eventHandlers[i]) {
document.removeEventListener("popupshown", eventHandlers[i], false);
}
}
for (let i = 0; i < inputValues.length; i++) {
if (inputValues[i][0] && !popupShown[i]) {
ok(false, "the property panel failed to show for inputValues[" + i + "]");
}
}
eventHandlers = popupshown = null;
executeSoon(finishTest);
}
function test() {
addTab(TEST_URI);
browser.addEventListener("load", tabLoad, true);
}

View File

@ -101,8 +101,8 @@ function testConsoleLoggingAPI(aMethod) {
// test for multiple arguments.
console[aMethod]("foo", "bar");
let node = outputNode.querySelectorAll(".hud-msg-node")[0];
ok(/foo bar/.test(node.textContent),
let node = outputNode.querySelector(".hud-msg-node");
ok(/"foo" "bar"/.test(node.textContent),
"Emitted both console arguments");
}

View File

@ -68,41 +68,43 @@ function testJSTerm()
jsterm.clearOutput();
jsterm.execute("'id=' + $('header').getAttribute('id')");
checkResult("id=header", "$() worked", 1);
checkResult('"id=header"', "$() worked", 1);
jsterm.clearOutput();
jsterm.execute("headerQuery = $$('h1')");
jsterm.execute("'length=' + headerQuery.length");
checkResult("length=1", "$$() worked", 2);
checkResult('"length=1"', "$$() worked", 2);
jsterm.clearOutput();
jsterm.execute("xpathQuery = $x('.//*', document.body);");
jsterm.execute("'headerFound=' + (xpathQuery[0] == headerQuery[0])");
checkResult("headerFound=true", "$x() worked", 2);
checkResult('"headerFound=true"', "$x() worked", 2);
// no jsterm.clearOutput() here as we clear the output using the clear() fn.
jsterm.execute("clear()");
checkResult("undefined", "clear() worked", 1);
let group = jsterm.outputNode.querySelector(".hud-group");
ok(!group, "clear() worked");
jsterm.clearOutput();
jsterm.execute("'keysResult=' + (keys({b:1})[0] == 'b')");
checkResult("keysResult=true", "keys() worked", 1);
checkResult('"keysResult=true"', "keys() worked", 1);
jsterm.clearOutput();
jsterm.execute("'valuesResult=' + (values({b:1})[0] == 1)");
checkResult("valuesResult=true", "values() worked", 1);
checkResult('"valuesResult=true"', "values() worked", 1);
jsterm.clearOutput();
jsterm.execute("help()");
checkResult("undefined", "help() worked", 1);
let output = jsterm.outputNode.querySelector(".jsterm-output-line");
ok(!group, "help() worked");
jsterm.clearOutput();
jsterm.execute("help");
checkResult("undefined", "help() worked", 1);
output = jsterm.outputNode.querySelector(".jsterm-output-line");
ok(!output, "help worked");
jsterm.clearOutput();
jsterm.execute("?");
checkResult("undefined", "help() worked", 1);
output = jsterm.outputNode.querySelector(".jsterm-output-line");
ok(!output, "? worked");
jsterm.clearOutput();
jsterm.execute("pprint({b:2, a:1})");
@ -130,12 +132,12 @@ function testJSTerm()
jsterm.clearOutput();
jsterm.execute("pprint(window)");
let labels = jsterm.outputNode.querySelectorAll(".jsterm-output-line");
ok(labels.length > 1, "more than one line of output for pprint(window)");
is(labels.length, 1, "one line of output for pprint(window)");
jsterm.clearOutput();
jsterm.execute("keys(window)");
let labels = jsterm.outputNode.querySelectorAll(".jsterm-output-line");
ok(labels.length, "more than 0 lines of output for keys(window)");
labels = jsterm.outputNode.querySelectorAll(".jsterm-output-line");
is(labels.length, 1, "one line of output for keys(window)");
jsterm.clearOutput();
jsterm.execute("pprint('hi')");

View File

@ -69,7 +69,7 @@ function testOutputOrder() {
/console\.log\('foo', 'bar'\);/.test(nodes[0].textContent);
let outputSecond =
/foo bar/.test(nodes[1].textContent);
/"foo" "bar"/.test(nodes[1].textContent);
ok(executedStringFirst && outputSecond, "executed string comes first");