Bug 939783 - console.trace() group traces even if part of trace is different; r=robcee

This commit is contained in:
Mihai Sucan 2014-01-23 23:37:32 +02:00
parent 2c23f853fb
commit d4f799b2bc
17 changed files with 476 additions and 157 deletions

View File

@ -48,7 +48,7 @@ function testFocus(sw, hud) {
function onMessage(event, messages) {
let msg = [...messages][0];
var loc = msg.querySelector(".location");
var loc = msg.querySelector(".message-location");
ok(loc, "location element exists");
is(loc.textContent.trim(), sw.Scratchpad.uniqueName + ":1",
"location value is correct");

View File

@ -145,6 +145,18 @@ ConsoleOutput.prototype = {
return this.owner.webConsoleClient;
},
/**
* Release an actor.
*
* @private
* @param string actorId
* The actor ID you want to release.
*/
_releaseObject: function(actorId)
{
this.owner._releaseObject(actorId);
},
/**
* Add a message to output.
*
@ -847,7 +859,7 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
let repeatNode = this.document.createElementNS(XHTML_NS, "span");
repeatNode.setAttribute("value", "1");
repeatNode.className = "repeats";
repeatNode.className = "message-repeats";
repeatNode.textContent = 1;
repeatNode._uid = this.getRepeatID();
return repeatNode;
@ -1077,6 +1089,134 @@ Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
},
}); // Messages.ConsoleGeneric.prototype
/**
* The ConsoleTrace message is used for console.trace() calls.
*
* @constructor
* @extends Messages.Simple
* @param object packet
* The Console API call packet received from the server.
*/
Messages.ConsoleTrace = function(packet)
{
let options = {
className: "consoleTrace cm-s-mozilla",
timestamp: packet.timeStamp,
category: "webdev",
severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
private: packet.private,
filterDuplicates: true,
location: {
url: packet.filename,
line: packet.lineNumber,
},
};
this._renderStack = this._renderStack.bind(this);
Messages.Simple.call(this, this._renderStack, options);
this._repeatID.consoleApiLevel = packet.level;
this._stacktrace = this._repeatID.stacktrace = packet.stacktrace;
this._arguments = packet.arguments;
};
Messages.ConsoleTrace.prototype = Heritage.extend(Messages.Simple.prototype,
{
/**
* Holds the stackframes received from the server.
*
* @private
* @type array
*/
_stacktrace: null,
/**
* Holds the arguments the content script passed to the console.trace()
* method. This array is cleared when the message is initialized, and
* associated actors are released.
*
* @private
* @type array
*/
_arguments: null,
init: function()
{
let result = Messages.Simple.prototype.init.apply(this, arguments);
// We ignore console.trace() arguments. Release object actors.
if (Array.isArray(this._arguments)) {
for (let arg of this._arguments) {
if (WebConsoleUtils.isActorGrip(arg)) {
this.output._releaseObject(arg.actor);
}
}
}
this._arguments = null;
return result;
},
/**
* Render the stack frames.
*
* @private
* @return DOMElement
*/
_renderStack: function()
{
let cmvar = this.document.createElementNS(XHTML_NS, "span");
cmvar.className = "cm-variable";
cmvar.textContent = "console";
let cmprop = this.document.createElementNS(XHTML_NS, "span");
cmprop.className = "cm-property";
cmprop.textContent = "trace";
let title = this.document.createElementNS(XHTML_NS, "span");
title.className = "title devtools-monospace";
title.appendChild(cmvar);
title.appendChild(this.document.createTextNode("."));
title.appendChild(cmprop);
title.appendChild(this.document.createTextNode("():"));
let repeatNode = Messages.Simple.prototype._renderRepeatNode.call(this);
let location = Messages.Simple.prototype._renderLocation.call(this);
if (location) {
location.target = "jsdebugger";
}
let widget = new Widgets.Stacktrace(this, this._stacktrace).render();
let body = this.document.createElementNS(XHTML_NS, "div");
body.appendChild(title);
if (repeatNode) {
body.appendChild(repeatNode);
}
if (location) {
body.appendChild(location);
}
body.appendChild(this.document.createTextNode("\n"));
let frag = this.document.createDocumentFragment();
frag.appendChild(body);
frag.appendChild(widget.element);
return frag;
},
_renderBody: function()
{
let body = Messages.Simple.prototype._renderBody.apply(this, arguments);
body.classList.remove("devtools-monospace");
return body;
},
// no-op for the message location and .repeats elements.
// |this._renderStack| handles customized message output.
_renderLocation: function() { },
_renderRepeatNode: function() { },
}); // Messages.ConsoleTrace.prototype
let Widgets = {};
@ -1354,6 +1494,91 @@ Widgets.LongString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
}); // Widgets.LongString.prototype
/**
* The stacktrace widget.
*
* @constructor
* @extends Widgets.BaseWidget
* @param object message
* The owning message.
* @param array stacktrace
* The stacktrace to display, array of frames as supplied by the server,
* over the remote protocol.
*/
Widgets.Stacktrace = function(message, stacktrace)
{
Widgets.BaseWidget.call(this, message);
this.stacktrace = stacktrace;
};
Widgets.Stacktrace.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
{
/**
* The stackframes received from the server.
* @type array
*/
stacktrace: null,
render: function()
{
if (this.element) {
return this;
}
let result = this.element = this.document.createElementNS(XHTML_NS, "ul");
result.className = "stacktrace devtools-monospace";
for (let frame of this.stacktrace) {
result.appendChild(this._renderFrame(frame));
}
return this;
},
/**
* Render a frame object received from the server.
*
* @param object frame
* The stack frame to display. This object should have the following
* properties: functionName, filename and lineNumber.
* @return DOMElement
* The DOM element to display for the given frame.
*/
_renderFrame: function(frame)
{
let fn = this.document.createElementNS(XHTML_NS, "span");
fn.className = "function";
if (frame.functionName) {
let span = this.document.createElementNS(XHTML_NS, "span");
span.className = "cm-variable";
span.textContent = frame.functionName;
fn.appendChild(span);
fn.appendChild(this.document.createTextNode("()"));
} else {
fn.classList.add("cm-comment");
fn.textContent = l10n.getStr("stacktrace.anonymousFunction");
}
let location = this.output.owner.createLocationNode(frame.filename,
frame.lineNumber,
"jsdebugger");
// .devtools-monospace sets font-size to 80%, however .body already has
// .devtools-monospace. If we keep it here, the location would be rendered
// smaller.
location.classList.remove("devtools-monospace");
let elem = this.document.createElementNS(XHTML_NS, "li");
elem.appendChild(fn);
elem.appendChild(location);
elem.appendChild(this.document.createTextNode("\n"));
return elem;
},
}); // Widgets.Stacktrace.prototype
function gSequenceId()
{
return gSequenceId.n++;

View File

@ -103,6 +103,7 @@ support-files =
test-bug_923281_console_log_filter.html
test-bug_923281_test1.js
test-bug_923281_test2.js
test-bug_939783_console_trace_duplicates.html
[browser_bug664688_sandbox_update_after_navigation.js]
[browser_bug_638949_copy_link_location.js]
@ -257,3 +258,4 @@ run-if = os == "mac"
[browser_webconsole_output_03.js]
[browser_webconsole_output_04.js]
[browser_webconsole_output_events.js]
[browser_webconsole_console_trace_duplicates.js]

View File

@ -70,7 +70,7 @@ function test()
{
let msg = [...results[0].matched][0];
ok(msg, "message element found");
let locationNode = msg.querySelector(".location");
let locationNode = msg.querySelector(".message-location");
ok(locationNode, "message location element found");
let title = locationNode.getAttribute("title");

View File

@ -61,7 +61,7 @@ function test()
let msg = [...results[0].matched][0];
ok(msg, "message element found for: " + result.text);
let locationNode = msg.querySelector(".location");
let locationNode = msg.querySelector(".message-location");
ok(locationNode, "message location element found");
EventUtils.synthesizeMouse(locationNode, 2, 2, {}, hud.iframeWindow);

View File

@ -6,30 +6,30 @@
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-585956-console-trace.html";
function test() {
addTab("data:text/html;charset=utf8,<p>hello");
browser.addEventListener("load", tabLoaded, true);
Task.spawn(runner).then(finishTest);
function tabLoaded() {
browser.removeEventListener("load", tabLoaded, true);
function* runner() {
let {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
let hud = yield openConsole(tab);
openConsole(null, function(hud) {
content.location = TEST_URI;
content.location = TEST_URI;
waitForMessages({
webconsole: hud,
messages: [{
name: "console.trace output",
consoleTrace: {
file: "test-bug-585956-console-trace.html",
fn: "window.foobar585956c",
},
}],
}).then(performChecks);
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.trace output",
consoleTrace: {
file: "test-bug-585956-console-trace.html",
fn: "window.foobar585956c",
},
}],
});
}
function performChecks(results) {
let node = [...results[0].matched][0];
let node = [...result.matched][0];
ok(node, "found trace log node");
let obj = node._messageObject;
ok(obj, "console.trace message object");
// The expected stack trace object.
let stacktrace = [
@ -39,11 +39,8 @@ function test() {
{ filename: TEST_URI, lineNumber: 21, functionName: null, language: 2 }
];
ok(node, "found trace log node");
ok(node._stacktrace, "found stacktrace object");
is(node._stacktrace.toSource(), stacktrace.toSource(), "stacktrace is correct");
ok(obj._stacktrace, "found stacktrace object");
is(obj._stacktrace.toSource(), stacktrace.toSource(), "stacktrace is correct");
isnot(node.textContent.indexOf("bug-585956"), -1, "found file name");
finishTest();
}
}

View File

@ -74,7 +74,7 @@ function testCSSPruning(hudRef) {
"repeated nodes pruned from repeatNodes");
let msg = [...result.matched][0];
let repeats = msg.querySelector(".repeats");
let repeats = msg.querySelector(".message-repeats");
is(repeats.getAttribute("value"), 1,
"repeated nodes pruned from repeatNodes (confirmed)");

View File

@ -34,8 +34,8 @@ function test() {
let exceptionMsg = [...exceptionRule.matched][0];
let consoleMsg = [...consoleRule.matched][0];
let nodes = [exceptionMsg.querySelector(".location"),
consoleMsg.querySelector(".location")];
let nodes = [exceptionMsg.querySelector(".message-location"),
consoleMsg.querySelector(".message-location")];
ok(nodes[0], ".location node for the exception message");
ok(nodes[1], ".location node for the console message");

View File

@ -41,10 +41,10 @@ function testViewSource(aHud)
}).then(([error1Rule, error2Rule]) => {
let error1Msg = [...error1Rule.matched][0];
let error2Msg = [...error2Rule.matched][0];
nodes = [error1Msg.querySelector(".location"),
error2Msg.querySelector(".location")];
ok(nodes[0], ".location node for the first error");
ok(nodes[1], ".location node for the second error");
nodes = [error1Msg.querySelector(".message-location"),
error2Msg.querySelector(".message-location")];
ok(nodes[0], ".message-location node for the first error");
ok(nodes[1], ".message-location node for the second error");
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);

View File

@ -0,0 +1,36 @@
/* 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/. */
function test() {
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug_939783_console_trace_duplicates.html";
Task.spawn(runner).then(finishTest);
function* runner() {
const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
const hud = yield openConsole(tab);
content.location = TEST_URI;
yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.trace output for foo1()",
text: "foo1()",
repeats: 2,
consoleTrace: {
file: "test-bug_939783_console_trace_duplicates.html",
fn: "foo3()",
},
}, {
name: "console.trace output for foo1b()",
text: "foo1b()",
consoleTrace: {
file: "test-bug_939783_console_trace_duplicates.html",
fn: "foo3()",
},
}],
});
}
}

View File

@ -53,7 +53,7 @@ function runTests(aToolbox)
let [matched] = [...messages[0].matched];
ok(matched, "Found logged message from Scratchpad");
let anchor = matched.querySelector("a.location");
let anchor = matched.querySelector("a.message-location");
aToolbox.on("scratchpad-selected", function selected() {
aToolbox.off("scratchpad-selected", selected);

View File

@ -47,7 +47,7 @@ function testViewSource(hud) {
function onMessage([result]) {
let msg = [...result.matched][0];
ok(msg, "error message");
let locationNode = msg.querySelector(".location");
let locationNode = msg.querySelector(".message-location");
ok(locationNode, "location node");
Services.ww.registerNotification(observer);

View File

@ -3,19 +3,15 @@
* 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/. */
let WebConsoleUtils, TargetFactory, require;
let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {require, TargetFactory} = devtools;
let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
let {Messages} = require("devtools/webconsole/console-output");
(() => {
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let utils = devtools.require("devtools/toolkit/webconsole/utils");
TargetFactory = devtools.TargetFactory;
WebConsoleUtils = utils.Utils;
require = devtools.require;
})();
// promise._reportErrors = true; // please never leave me.
let gPendingOutputTest = 0;
@ -283,7 +279,7 @@ function dumpConsoles()
function dumpMessageElement(aMessage)
{
let text = aMessage.textContent;
let repeats = aMessage.querySelector(".repeats");
let repeats = aMessage.querySelector(".message-repeats");
if (repeats) {
repeats = repeats.getAttribute("value");
}
@ -932,44 +928,44 @@ function waitForMessages(aOptions)
let elemText = aElement.textContent;
let trace = aRule.consoleTrace;
if (!checkText("Stack trace from ", elemText)) {
if (!checkText("console.trace():", elemText)) {
return false;
}
let clickable = aElement.querySelector(".body a");
if (!clickable) {
ok(false, "console.trace() message is missing .hud-clickable");
displayErrorContext(aRule, aElement);
return false;
}
aRule.clickableElements = [clickable];
if (trace.file &&
!checkText("from " + trace.file + ", ", elemText)) {
ok(false, "console.trace() message is missing the file name: " +
trace.file);
displayErrorContext(aRule, aElement);
return false;
let frame = aElement.querySelector(".stacktrace li:first-child");
if (trace.file) {
let file = frame.querySelector(".message-location").title;
if (!checkText(trace.file, file)) {
ok(false, "console.trace() message is missing the file name: " +
trace.file);
displayErrorContext(aRule, aElement);
return false;
}
}
if (trace.fn &&
!checkText(", function " + trace.fn + ", ", elemText)) {
ok(false, "console.trace() message is missing the function name: " +
trace.fn);
displayErrorContext(aRule, aElement);
return false;
if (trace.fn) {
let fn = frame.querySelector(".function").textContent;
if (!checkText(trace.fn, fn)) {
ok(false, "console.trace() message is missing the function name: " +
trace.fn);
displayErrorContext(aRule, aElement);
return false;
}
}
if (trace.line &&
!checkText(", line " + trace.line + ".", elemText)) {
ok(false, "console.trace() message is missing the line number: " +
trace.line);
displayErrorContext(aRule, aElement);
return false;
if (trace.line) {
let line = frame.querySelector(".message-location").sourceLine;
if (!checkText(trace.line, line)) {
ok(false, "console.trace() message is missing the line number: " +
trace.line);
displayErrorContext(aRule, aElement);
return false;
}
}
aRule.category = CATEGORY_WEBDEV;
aRule.severity = SEVERITY_LOG;
aRule.type = Messages.ConsoleTrace;
return true;
}
@ -1038,7 +1034,7 @@ function waitForMessages(aOptions)
function checkSource(aRule, aElement)
{
let location = aElement.querySelector(".location");
let location = aElement.querySelector(".message-location");
if (!location) {
return false;
}
@ -1090,16 +1086,23 @@ function waitForMessages(aOptions)
return false;
}
let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime ||
aRule.consoleTimeEnd);
// The rule tries to match the newer types of messages, based on their
// object constructor.
if (aRule.type && (!aElement._messageObject ||
!(aElement._messageObject instanceof aRule.type))) {
return false;
if (aRule.type) {
if (!aElement._messageObject ||
!(aElement._messageObject instanceof aRule.type)) {
if (partialMatch) {
ok(false, "message type for rule: " + displayRule(aRule));
displayErrorContext(aRule, aElement);
}
return false;
}
partialMatch = true;
}
let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime ||
aRule.consoleTimeEnd || aRule.type);
if ("category" in aRule && aElement.category != aRule.category) {
if (partialMatch) {
is(aElement.category, aRule.category,
@ -1124,7 +1127,7 @@ function waitForMessages(aOptions)
}
if ("repeats" in aRule) {
let repeats = aElement.querySelector(".repeats");
let repeats = aElement.querySelector(".message-repeats");
if (!repeats || repeats.getAttribute("value") != aRule.repeats) {
return false;
}
@ -1180,7 +1183,7 @@ function waitForMessages(aOptions)
function onMessagesAdded(aEvent, aNewElements)
{
for (let elem of aNewElements) {
let location = elem.querySelector(".location");
let location = elem.querySelector(".message-location");
if (location) {
let url = location.title;
// Prevent recursion with the browser console and any potential

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Web Console test for bug 939783 - different console.trace() calls
wrongly filtered as duplicates</title>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<script type="application/javascript">
function foo1() {
foo2();
}
function foo1b() {
foo2();
}
function foo2() {
foo3();
}
function foo3() {
console.trace();
}
foo1(); foo1();
foo1b();
</script>
</head>
<body>
<p>Web Console test for bug 939783 - different console.trace() calls
wrongly filtered as duplicates</p>
</body>
</html>

View File

@ -1056,7 +1056,7 @@ WebConsoleFrame.prototype = {
mergeFilteredMessageNode:
function WCF_mergeFilteredMessageNode(aOriginal, aFiltered)
{
let repeatNode = aOriginal.getElementsByClassName("repeats")[0];
let repeatNode = aOriginal.getElementsByClassName("message-repeats")[0];
if (!repeatNode) {
return; // no repeat node, return early.
}
@ -1081,7 +1081,7 @@ WebConsoleFrame.prototype = {
*/
_filterRepeatedMessage: function WCF__filterRepeatedMessage(aNode)
{
let repeatNode = aNode.getElementsByClassName("repeats")[0];
let repeatNode = aNode.getElementsByClassName("message-repeats")[0];
if (!repeatNode) {
return null;
}
@ -1105,7 +1105,7 @@ WebConsoleFrame.prototype = {
return null;
}
let lastRepeatNode = lastMessage.getElementsByClassName("repeats")[0];
let lastRepeatNode = lastMessage.getElementsByClassName("message-repeats")[0];
if (lastRepeatNode && lastRepeatNode._uid == uid) {
dupeNode = lastMessage;
}
@ -1191,6 +1191,11 @@ WebConsoleFrame.prototype = {
node = msg.init(this.output).render().element;
break;
}
case "trace": {
let msg = new Messages.ConsoleTrace(aMessage);
node = msg.init(this.output).render().element;
break;
}
case "dir": {
body = { arguments: args };
let clipboardArray = [];
@ -1201,38 +1206,6 @@ WebConsoleFrame.prototype = {
break;
}
case "trace": {
let filename = WebConsoleUtils.abbreviateSourceURL(aMessage.filename);
let functionName = aMessage.functionName ||
l10n.getStr("stacktrace.anonymousFunction");
body = this.document.createElementNS(XHTML_NS, "a");
body.setAttribute("aria-haspopup", true);
body.href = "#";
body.draggable = false;
body.textContent = l10n.getFormatStr("stacktrace.outputMessage",
[filename, functionName,
sourceLine]);
this._addMessageLinkCallback(body, () => {
this.jsterm.openVariablesView({
rawObject: aMessage.stacktrace,
autofocus: true,
});
});
clipboardText = body.textContent + "\n";
aMessage.stacktrace.forEach(function(aFrame) {
clipboardText += aFrame.filename + " :: " +
aFrame.functionName + " :: " +
aFrame.lineNumber + "\n";
});
clipboardText = clipboardText.trimRight();
break;
}
case "group":
case "groupCollapsed":
clipboardText = body = aMessage.groupName;
@ -1281,7 +1254,6 @@ WebConsoleFrame.prototype = {
case "group":
case "groupCollapsed":
case "groupEnd":
case "trace":
case "time":
case "timeEnd":
for (let actor of objectActors) {
@ -1307,15 +1279,11 @@ WebConsoleFrame.prototype = {
node._objectActors = objectActors;
if (!node._messageObject) {
let repeatNode = node.getElementsByClassName("repeats")[0];
let repeatNode = node.getElementsByClassName("message-repeats")[0];
repeatNode._uid += [...objectActors].join("-");
}
}
if (level == "trace") {
node._stacktrace = aMessage.stacktrace;
}
return node;
},
@ -2388,7 +2356,7 @@ WebConsoleFrame.prototype = {
if (aNode.category == CATEGORY_CSS ||
aNode.category == CATEGORY_SECURITY) {
let repeatNode = aNode.getElementsByClassName("repeats")[0];
let repeatNode = aNode.getElementsByClassName("message-repeats")[0];
if (repeatNode && repeatNode._uid) {
delete this._repeatNodes[repeatNode._uid];
}
@ -2501,7 +2469,7 @@ WebConsoleFrame.prototype = {
!(aCategory == CATEGORY_CSS && aSeverity == SEVERITY_LOG)) {
repeatNode = this.document.createElementNS(XHTML_NS, "span");
repeatNode.setAttribute("value", "1");
repeatNode.className = "repeats";
repeatNode.className = "message-repeats";
repeatNode.textContent = 1;
repeatNode._uid = [bodyNode.textContent, aCategory, aSeverity, aLevel,
aSourceURL, aSourceLine].join(":");
@ -2567,11 +2535,18 @@ WebConsoleFrame.prototype = {
* @param number aSourceLine [optional]
* The line number on which the error occurred. If zero or omitted,
* there is no line number associated with this message.
* @param string aTarget [optional]
* Tells which tool to open the link with, on click. Supported tools:
* jsdebugger, styleeditor, scratchpad.
* @return nsIDOMNode
* The new anchor element, ready to be added to the message node.
*/
createLocationNode: function WCF_createLocationNode(aSourceURL, aSourceLine)
createLocationNode:
function WCF_createLocationNode(aSourceURL, aSourceLine, aTarget)
{
if (!aSourceURL) {
aSourceURL = "";
}
let locationNode = this.document.createElementNS(XHTML_NS, "a");
let filenameNode = this.document.createElementNS(XHTML_NS, "span");
@ -2592,30 +2567,39 @@ WebConsoleFrame.prototype = {
}
filenameNode.className = "filename";
filenameNode.textContent = " " + filename;
filenameNode.textContent = " " + (filename || l10n.getStr("unknownLocation"));
locationNode.appendChild(filenameNode);
locationNode.href = isScratchpad ? "#" : fullURL;
locationNode.href = isScratchpad || !fullURL ? "#" : fullURL;
locationNode.draggable = false;
locationNode.target = aTarget;
locationNode.setAttribute("title", aSourceURL);
locationNode.className = "location theme-link devtools-monospace";
locationNode.className = "message-location theme-link devtools-monospace";
// Make the location clickable.
this._addMessageLinkCallback(locationNode, () => {
if (isScratchpad) {
let onClick = () => {
let target = locationNode.target;
if (target == "scratchpad" || isScratchpad) {
this.owner.viewSourceInScratchpad(aSourceURL);
return;
}
else if (locationNode.parentNode.category == CATEGORY_CSS) {
let category = locationNode.parentNode.category;
if (target == "styleeditor" || category == CATEGORY_CSS) {
this.owner.viewSourceInStyleEditor(fullURL, aSourceLine);
}
else if (locationNode.parentNode.category == CATEGORY_JS ||
locationNode.parentNode.category == CATEGORY_WEBDEV) {
else if (target == "jsdebugger" ||
category == CATEGORY_JS || category == CATEGORY_WEBDEV) {
this.owner.viewSourceInDebugger(fullURL, aSourceLine);
}
else {
this.owner.viewSource(fullURL, aSourceLine);
}
});
};
if (fullURL) {
this._addMessageLinkCallback(locationNode, onClick);
}
if (aSourceLine) {
let lineNumberNode = this.document.createElementNS(XHTML_NS, "span");

View File

@ -102,15 +102,13 @@ reflow.messageLinkText=function %1$S, %2$S line %3$S
# LOCALIZATION NOTE (stacktrace.anonymousFunction): this string is used to
# display JavaScript functions that have no given name - they are said to be
# anonymous. See also stacktrace.outputMessage.
# anonymous. Test console.trace() in the webconsole.
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. Parameters: %1$S is the file name, %2$S
# is the function name, %3$S is the line number.
stacktrace.outputMessage=Stack trace from %1$S, function %2$S, line %3$S.
# LOCALIZATION NOTE (unknownLocation): this string is used to
# display messages with sources that have an unknown location, eg. from
# console.trace() calls.
unknownLocation=<unknown>
# LOCALIZATION NOTE (timerStarted): this string is used to display the result
# of the console.time() call. Parameters: %S is the name of the timer.

View File

@ -46,7 +46,7 @@ a {
}
/* The red bubble that shows the number of times a message is repeated */
.message > .repeats {
.message-repeats {
-moz-user-select: none;
flex: 0 0 auto;
margin: 2px 6px;
@ -60,11 +60,11 @@ a {
font-weight: 600;
}
.message > .repeats[value="1"] {
.message-repeats[value="1"] {
display: none;
}
.message > .location {
.message-location {
-moz-margin-start: 6px;
display: flex;
flex: 0 0 auto;
@ -74,21 +74,21 @@ a {
margin-top: 4px;
color: -moz-nativehyperlinktext;
text-decoration: none;
}
.message > .location:hover,
.message > .location:focus {
text-decoration: underline;
}
.message > .location > .filename {
text-overflow: ellipsis;
text-align: end;
overflow: hidden;
white-space: nowrap;
}
.message > .location > .line-number {
.message-location:hover,
.message-location:focus {
text-decoration: underline;
}
.message-location > .filename {
text-overflow: ellipsis;
text-align: end;
overflow: hidden;
}
.message-location > .line-number {
flex: 0 0 auto;
}
@ -336,6 +336,37 @@ a {
text-decoration: none;
}
.consoleTrace .body > div {
display: flex;
margin-bottom: 5px;
}
.consoleTrace .title {
display: block;
flex: 1 1 auto;
}
.stacktrace {
list-style: none;
padding: 0 1em 0 1.5em;
margin: 0;
max-height: 10em;
overflow-y: auto;
border: 1px solid rgba(128, 128, 128, .5);
border-radius: 3px;
}
.stacktrace li {
display: flex;
margin: 0;
}
.stacktrace .function {
display: block;
flex: 1 1 auto;
}
/* Replace these values with CSS variables as available */
.theme-dark .jsterm-input-container {
background-color: #252c33; /* tabToolbarBackgroundColor */
@ -358,6 +389,10 @@ a {
border-color: #333;
}
.theme-dark .stacktrace {
border-color: #333;
}
.theme-light .jsterm-input-container {
background-color: #fff; /* mainBackgroundColor */
border-color: ThreeDShadow;
@ -379,6 +414,10 @@ a {
border-color: #ccc;
}
.theme-light .stacktrace {
border-color: #ccc;
}
@media (max-width: 500px) {
.message > .timestamp {
display: none;