mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1184172 - Show stackframe for errors in the webconsole. r=past
This commit is contained in:
parent
162069381f
commit
419641bcc2
@ -675,6 +675,7 @@ Messages.NavigationMarker.prototype = Heritage.extend(Messages.BaseMessage.proto
|
||||
* handler.
|
||||
* - location: object that tells the message source: url, line, column
|
||||
* and lineText.
|
||||
* - stack: array that tells the message source stack.
|
||||
* - className: (string) additional element class names for styling
|
||||
* purposes.
|
||||
* - private: (boolean) mark this as a private message.
|
||||
@ -688,6 +689,7 @@ Messages.Simple = function(message, options = {})
|
||||
this.category = options.category;
|
||||
this.severity = options.severity;
|
||||
this.location = options.location;
|
||||
this.stack = options.stack;
|
||||
this.timestamp = options.timestamp || Date.now();
|
||||
this.prefix = options.prefix;
|
||||
this.private = !!options.private;
|
||||
@ -697,6 +699,8 @@ Messages.Simple = function(message, options = {})
|
||||
this._link = options.link;
|
||||
this._linkCallback = options.linkCallback;
|
||||
this._filterDuplicates = options.filterDuplicates;
|
||||
|
||||
this._onClickCollapsible = this._onClickCollapsible.bind(this);
|
||||
};
|
||||
|
||||
Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
@ -719,6 +723,14 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
*/
|
||||
location: null,
|
||||
|
||||
/**
|
||||
* Holds the stackframes received from the server.
|
||||
*
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
stack: null,
|
||||
|
||||
/**
|
||||
* Message prefix
|
||||
* @type string|null
|
||||
@ -810,6 +822,20 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tells if the message can be expanded/collapsed.
|
||||
* @type boolean
|
||||
*/
|
||||
collapsible: false,
|
||||
|
||||
/**
|
||||
* Getter that tells if this message is collapsed - no details are shown.
|
||||
* @type boolean
|
||||
*/
|
||||
get collapsed() {
|
||||
return this.collapsible && this.element && !this.element.hasAttribute("open");
|
||||
},
|
||||
|
||||
_initRepeatID: function()
|
||||
{
|
||||
if (!this._filterDuplicates) {
|
||||
@ -854,6 +880,9 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
let icon = this.document.createElementNS(XHTML_NS, "span");
|
||||
icon.className = "icon";
|
||||
icon.title = l10n.getStr("severity." + this._severityNameCompat);
|
||||
if (this.stack) {
|
||||
icon.addEventListener("click", this._onClickCollapsible);
|
||||
}
|
||||
|
||||
let prefixNode;
|
||||
if (this.prefix) {
|
||||
@ -886,6 +915,18 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
if (prefixNode) {
|
||||
this.element.appendChild(prefixNode);
|
||||
}
|
||||
|
||||
if (this.stack) {
|
||||
let twisty = this.document.createElementNS(XHTML_NS, "a");
|
||||
twisty.className = "theme-twisty";
|
||||
twisty.href = "#";
|
||||
twisty.title = l10n.getStr("messageToggleDetails");
|
||||
twisty.addEventListener("click", this._onClickCollapsible);
|
||||
this.element.appendChild(twisty);
|
||||
this.collapsible = true;
|
||||
this.element.setAttribute("collapsible", true);
|
||||
}
|
||||
|
||||
this.element.appendChild(body);
|
||||
if (repeatNode) {
|
||||
this.element.appendChild(repeatNode);
|
||||
@ -893,6 +934,7 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
if (location) {
|
||||
this.element.appendChild(location);
|
||||
}
|
||||
|
||||
this.element.appendChild(this.document.createTextNode("\n"));
|
||||
|
||||
this.element.clipboardText = this.element.textContent;
|
||||
@ -944,6 +986,12 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
container.textContent = this._message;
|
||||
}
|
||||
|
||||
if (this.stack) {
|
||||
let stack = new Widgets.Stacktrace(this, this.stack).render().element;
|
||||
body.appendChild(this.document.createTextNode("\n"));
|
||||
body.appendChild(stack);
|
||||
}
|
||||
|
||||
return body;
|
||||
},
|
||||
|
||||
@ -988,6 +1036,36 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
|
||||
line: line,
|
||||
column: column});
|
||||
},
|
||||
|
||||
/**
|
||||
* The click event handler for the message expander arrow element. This method
|
||||
* toggles the display of message details.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMEvent ev
|
||||
* The DOM event object.
|
||||
* @see this.toggleDetails()
|
||||
*/
|
||||
_onClickCollapsible: function(ev)
|
||||
{
|
||||
ev.preventDefault();
|
||||
this.toggleDetails();
|
||||
},
|
||||
|
||||
/**
|
||||
* Expand/collapse message details.
|
||||
*/
|
||||
toggleDetails: function()
|
||||
{
|
||||
let twisty = this.element.querySelector(".theme-twisty");
|
||||
if (this.element.hasAttribute("open")) {
|
||||
this.element.removeAttribute("open");
|
||||
twisty.removeAttribute("open");
|
||||
} else {
|
||||
this.element.setAttribute("open", true);
|
||||
twisty.setAttribute("open", true);
|
||||
}
|
||||
},
|
||||
}); // Messages.Simple.prototype
|
||||
|
||||
|
||||
@ -1330,30 +1408,13 @@ Messages.ConsoleGeneric = function(packet)
|
||||
|
||||
this._repeatID.consoleApiLevel = packet.level;
|
||||
this._repeatID.styles = packet.styles;
|
||||
this._stacktrace = this._repeatID.stacktrace = packet.stacktrace;
|
||||
this.stack = this._repeatID.stacktrace = packet.stacktrace;
|
||||
this._styles = packet.styles || [];
|
||||
|
||||
this._onClickCollapsible = this._onClickCollapsible.bind(this);
|
||||
};
|
||||
|
||||
Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
|
||||
{
|
||||
_styles: null,
|
||||
_stacktrace: null,
|
||||
|
||||
/**
|
||||
* Tells if the message can be expanded/collapsed.
|
||||
* @type boolean
|
||||
*/
|
||||
collapsible: false,
|
||||
|
||||
/**
|
||||
* Getter that tells if this message is collapsed - no details are shown.
|
||||
* @type boolean
|
||||
*/
|
||||
get collapsed() {
|
||||
return this.collapsible && this.element && !this.element.hasAttribute("open");
|
||||
},
|
||||
|
||||
_renderBodyPieceSeparator: function()
|
||||
{
|
||||
@ -1373,25 +1434,9 @@ Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
|
||||
location.target = "jsdebugger";
|
||||
}
|
||||
|
||||
let stack = null;
|
||||
let twisty = null;
|
||||
if (this._stacktrace && this._stacktrace.length > 0) {
|
||||
stack = new Widgets.Stacktrace(this, this._stacktrace).render().element;
|
||||
|
||||
twisty = this.document.createElementNS(XHTML_NS, "a");
|
||||
twisty.className = "theme-twisty";
|
||||
twisty.href = "#";
|
||||
twisty.title = l10n.getStr("messageToggleDetails");
|
||||
twisty.addEventListener("click", this._onClickCollapsible);
|
||||
}
|
||||
|
||||
let flex = this.document.createElementNS(XHTML_NS, "span");
|
||||
flex.className = "message-flex-body";
|
||||
|
||||
if (twisty) {
|
||||
flex.appendChild(twisty);
|
||||
}
|
||||
|
||||
flex.appendChild(msg);
|
||||
|
||||
if (repeatNode) {
|
||||
@ -1404,24 +1449,11 @@ Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
|
||||
let result = this.document.createDocumentFragment();
|
||||
result.appendChild(flex);
|
||||
|
||||
if (stack) {
|
||||
result.appendChild(this.document.createTextNode("\n"));
|
||||
result.appendChild(stack);
|
||||
}
|
||||
|
||||
this._message = result;
|
||||
this._stacktrace = null;
|
||||
|
||||
Messages.Simple.prototype.render.call(this);
|
||||
|
||||
if (stack) {
|
||||
this.collapsible = true;
|
||||
this.element.setAttribute("collapsible", true);
|
||||
|
||||
let icon = this.element.querySelector(".icon");
|
||||
icon.addEventListener("click", this._onClickCollapsible);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
@ -1484,36 +1516,6 @@ Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
|
||||
_renderLocation: function() { },
|
||||
_renderRepeatNode: function() { },
|
||||
|
||||
/**
|
||||
* Expand/collapse message details.
|
||||
*/
|
||||
toggleDetails: function()
|
||||
{
|
||||
let twisty = this.element.querySelector(".theme-twisty");
|
||||
if (this.element.hasAttribute("open")) {
|
||||
this.element.removeAttribute("open");
|
||||
twisty.removeAttribute("open");
|
||||
} else {
|
||||
this.element.setAttribute("open", true);
|
||||
twisty.setAttribute("open", true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The click event handler for the message expander arrow element. This method
|
||||
* toggles the display of message details.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMEvent ev
|
||||
* The DOM event object.
|
||||
* @see this.toggleDetails()
|
||||
*/
|
||||
_onClickCollapsible: function(ev)
|
||||
{
|
||||
ev.preventDefault();
|
||||
this.toggleDetails();
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a style attribute value, return a cleaned up version of the string
|
||||
* such that:
|
||||
|
@ -129,6 +129,7 @@ support-files =
|
||||
test-bug_1050691_click_function_to_source.html
|
||||
test-bug_1050691_click_function_to_source.js
|
||||
test-console-api-stackframe.html
|
||||
test-exception-stackframe.html
|
||||
test_bug_1010953_cspro.html^headers^
|
||||
test_bug_1010953_cspro.html
|
||||
test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
|
||||
@ -383,6 +384,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_autocomplete_crossdomain_iframe.js]
|
||||
[browser_webconsole_console_custom_styles.js]
|
||||
[browser_webconsole_console_api_stackframe.js]
|
||||
[browser_webconsole_exception_stackframe.js]
|
||||
[browser_webconsole_column_numbers.js]
|
||||
[browser_console_open_or_focus.js]
|
||||
[browser_webconsole_bug_922212_console_dirxml.js]
|
||||
|
@ -57,7 +57,7 @@ function test() {
|
||||
|
||||
let msg = [...result.matched][0];
|
||||
ok(msg, "message element found");
|
||||
let locationNode = msg.querySelector(".message-location");
|
||||
let locationNode = msg.querySelector(".message > .message-location");
|
||||
ok(locationNode, "message location element found");
|
||||
|
||||
let title = locationNode.getAttribute("title");
|
||||
@ -78,7 +78,7 @@ function test() {
|
||||
browserconsole.iframeWindow);
|
||||
|
||||
info("wait for click on locationNode");
|
||||
yield clickPromise;
|
||||
yield clickPromise.promise;
|
||||
|
||||
info("view-source url: " + URL);
|
||||
ok(URL, "we have some source URL after the click");
|
||||
|
@ -58,7 +58,7 @@ function test() {
|
||||
|
||||
let msg = [...results[0].matched][0];
|
||||
ok(msg, "message element found for: " + result.text);
|
||||
let locationNode = msg.querySelector(".message-location");
|
||||
let locationNode = msg.querySelector(".message > .message-location");
|
||||
ok(locationNode, "message location element found");
|
||||
|
||||
EventUtils.synthesizeMouse(locationNode, 2, 2, {}, hud.iframeWindow);
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the console receive exceptions include a stackframe.
|
||||
// See bug 1184172.
|
||||
|
||||
// On e10s, the exception is triggered in child process
|
||||
// and is ignored by test harness
|
||||
if (!Services.appinfo.browserTabsRemoteAutostart) {
|
||||
expectUncaughtException();
|
||||
}
|
||||
|
||||
function test() {
|
||||
let hud;
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
|
||||
"test/test-exception-stackframe.html";
|
||||
const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
|
||||
|
||||
Task.spawn(runner).then(finishTest);
|
||||
|
||||
function* runner() {
|
||||
const {tab} = yield loadTab(TEST_URI);
|
||||
hud = yield openConsole(tab);
|
||||
|
||||
const stack = [{
|
||||
file: TEST_FILE,
|
||||
fn: "thirdCall",
|
||||
line: 21,
|
||||
}, {
|
||||
file: TEST_FILE,
|
||||
fn: "secondCall",
|
||||
line: 17,
|
||||
}, {
|
||||
file: TEST_FILE,
|
||||
fn: "firstCall",
|
||||
line: 12,
|
||||
}];
|
||||
|
||||
let results = yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "nonExistingMethodCall is not defined",
|
||||
category: CATEGORY_JS,
|
||||
severity: SEVERITY_ERROR,
|
||||
collapsible: true,
|
||||
stacktrace: stack,
|
||||
}],
|
||||
});
|
||||
|
||||
let elem = [...results[0].matched][0];
|
||||
ok(elem, "message element");
|
||||
|
||||
let msg = elem._messageObject;
|
||||
ok(msg, "message object");
|
||||
|
||||
ok(msg.collapsed, "message is collapsed");
|
||||
|
||||
msg.toggleDetails();
|
||||
|
||||
ok(!msg.collapsed, "message is not collapsed");
|
||||
|
||||
msg.toggleDetails();
|
||||
|
||||
ok(msg.collapsed, "message is collapsed");
|
||||
|
||||
yield closeConsole(tab);
|
||||
}
|
||||
}
|
@ -1079,7 +1079,7 @@ function waitForMessages(options) {
|
||||
let file = frame.querySelector(".message-location").title;
|
||||
if (!checkText(expected.file, file)) {
|
||||
ok(false, "frame #" + i + " does not match file name: " +
|
||||
expected.file);
|
||||
expected.file + " != " + file);
|
||||
displayErrorContext(rule, element);
|
||||
return false;
|
||||
}
|
||||
@ -1089,7 +1089,7 @@ function waitForMessages(options) {
|
||||
let fn = frame.querySelector(".function").textContent;
|
||||
if (!checkText(expected.fn, fn)) {
|
||||
ok(false, "frame #" + i + " does not match the function name: " +
|
||||
expected.fn);
|
||||
expected.fn + " != " + fn);
|
||||
displayErrorContext(rule, element);
|
||||
return false;
|
||||
}
|
||||
@ -1099,7 +1099,7 @@ function waitForMessages(options) {
|
||||
let line = frame.querySelector(".message-location").sourceLine;
|
||||
if (!checkText(expected.line, line)) {
|
||||
ok(false, "frame #" + i + " does not match the line number: " +
|
||||
expected.line);
|
||||
expected.line + " != " + line);
|
||||
displayErrorContext(rule, element);
|
||||
return false;
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<!--
|
||||
- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<title>Test for bug 1184172 - stacktraces for exceptions</title>
|
||||
<script>
|
||||
function firstCall() {
|
||||
secondCall();
|
||||
}
|
||||
|
||||
// Check anonymous functions
|
||||
var secondCall = function () {
|
||||
thirdCall();
|
||||
}
|
||||
|
||||
function thirdCall() {
|
||||
nonExistingMethodCall();
|
||||
}
|
||||
|
||||
window.onload = firstCall;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello world!</p>
|
||||
</body>
|
||||
</html>
|
@ -1528,6 +1528,7 @@ WebConsoleFrame.prototype = {
|
||||
line: aScriptError.lineNumber,
|
||||
column: aScriptError.columnNumber
|
||||
},
|
||||
stack: aScriptError.stacktrace,
|
||||
category: category,
|
||||
severity: severity,
|
||||
timestamp: aScriptError.timeStamp,
|
||||
|
@ -1288,6 +1288,21 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError)
|
||||
{
|
||||
let stack = null;
|
||||
// Convert stack objects to the JSON attributes expected by client code
|
||||
if (aPageError.stack) {
|
||||
stack = [];
|
||||
let s = aPageError.stack;
|
||||
while (s !== null) {
|
||||
stack.push({
|
||||
filename: s.source,
|
||||
lineNumber: s.line,
|
||||
columnNumber: s.column,
|
||||
functionName: s.functionDisplayName
|
||||
});
|
||||
s = s.parent;
|
||||
}
|
||||
}
|
||||
let lineText = aPageError.sourceLine;
|
||||
if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
|
||||
lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
|
||||
@ -1307,6 +1322,7 @@ WebConsoleActor.prototype =
|
||||
strict: !!(aPageError.flags & aPageError.strictFlag),
|
||||
info: !!(aPageError.flags & aPageError.infoFlag),
|
||||
private: aPageError.isFromPrivateWindow,
|
||||
stacktrace: stack
|
||||
};
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user