зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1294499
- New console frontend: Add collapsible stacktrace for console.error/trace messages. r=linclark
MozReview-Commit-ID: LctpJdFtxX0
This commit is contained in:
Родитель
6a66311928
Коммит
d8d0dd3e36
|
@ -13,7 +13,9 @@ const { IdGenerator } = require("devtools/client/webconsole/new-console-output/u
|
||||||
|
|
||||||
const {
|
const {
|
||||||
MESSAGE_ADD,
|
MESSAGE_ADD,
|
||||||
MESSAGES_CLEAR
|
MESSAGES_CLEAR,
|
||||||
|
MESSAGE_OPEN,
|
||||||
|
MESSAGE_CLOSE,
|
||||||
} = require("../constants");
|
} = require("../constants");
|
||||||
|
|
||||||
const defaultIdGenerator = new IdGenerator();
|
const defaultIdGenerator = new IdGenerator();
|
||||||
|
@ -36,5 +38,21 @@ function messagesClear() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function messageOpen(id) {
|
||||||
|
return {
|
||||||
|
type: MESSAGE_OPEN,
|
||||||
|
id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageClose(id) {
|
||||||
|
return {
|
||||||
|
type: MESSAGE_CLOSE,
|
||||||
|
id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
exports.messageAdd = messageAdd;
|
exports.messageAdd = messageAdd;
|
||||||
exports.messagesClear = messagesClear;
|
exports.messagesClear = messagesClear;
|
||||||
|
exports.messageOpen = messageOpen;
|
||||||
|
exports.messageClose = messageClose;
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// React & Redux
|
||||||
|
const {
|
||||||
|
createClass,
|
||||||
|
DOM: dom,
|
||||||
|
PropTypes,
|
||||||
|
} = require("devtools/client/shared/vendor/react");
|
||||||
|
|
||||||
|
const CollapseButton = createClass({
|
||||||
|
|
||||||
|
displayName: "CollapseButton",
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
open: PropTypes.bool.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
const { title, open, onClick } = this.props;
|
||||||
|
|
||||||
|
let classes = ["theme-twisty"];
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
classes.push("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dom.a({
|
||||||
|
className: classes.join(" "),
|
||||||
|
onClick: onClick,
|
||||||
|
title
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.CollapseButton = CollapseButton;
|
|
@ -12,7 +12,7 @@ const {
|
||||||
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
|
||||||
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
|
const { getAllMessages, getAllMessagesUiById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
|
||||||
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
|
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
|
||||||
|
|
||||||
const ConsoleOutput = createClass({
|
const ConsoleOutput = createClass({
|
||||||
|
@ -41,11 +41,24 @@ const ConsoleOutput = createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {messages, sourceMapService, onViewSourceInDebugger} = this.props;
|
let {
|
||||||
|
dispatch,
|
||||||
|
messages,
|
||||||
|
messagesUi,
|
||||||
|
sourceMapService,
|
||||||
|
onViewSourceInDebugger
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
let messageNodes = messages.map(function (message) {
|
let messageNodes = messages.map(function (message) {
|
||||||
return (
|
return (
|
||||||
MessageContainer({ message, key: message.id,
|
MessageContainer({
|
||||||
sourceMapService, onViewSourceInDebugger })
|
dispatch,
|
||||||
|
message,
|
||||||
|
key: message.id,
|
||||||
|
sourceMapService,
|
||||||
|
onViewSourceInDebugger,
|
||||||
|
open: messagesUi.includes(message.id)
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
|
@ -63,7 +76,8 @@ function isScrolledToBottom(outputNode, scrollNode) {
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
messages: getAllMessages(state)
|
messages: getAllMessages(state),
|
||||||
|
messagesUi: getAllMessagesUiById(state)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,16 +33,31 @@ const MessageContainer = createClass({
|
||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
sourceMapService: PropTypes.object,
|
sourceMapService: PropTypes.object,
|
||||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||||
|
open: PropTypes.bool.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
return this.props.message.repeat !== nextProps.message.repeat;
|
return this.props.message.repeat !== nextProps.message.repeat
|
||||||
|
|| this.props.open !== nextProps.open;
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { message, sourceMapService, onViewSourceInDebugger } = this.props;
|
const {
|
||||||
|
dispatch,
|
||||||
|
message,
|
||||||
|
sourceMapService,
|
||||||
|
onViewSourceInDebugger,
|
||||||
|
open
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
let MessageComponent = createFactory(getMessageComponent(message));
|
let MessageComponent = createFactory(getMessageComponent(message));
|
||||||
return MessageComponent({ message, sourceMapService, onViewSourceInDebugger });
|
return MessageComponent({
|
||||||
|
dispatch,
|
||||||
|
message,
|
||||||
|
sourceMapService,
|
||||||
|
onViewSourceInDebugger,
|
||||||
|
open
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ const StackTrace = createFactory(require("devtools/client/shared/components/stac
|
||||||
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
|
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
|
||||||
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
|
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
|
||||||
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
|
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
|
||||||
|
const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
|
||||||
|
const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||||
|
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
|
||||||
|
|
||||||
ConsoleApiCall.displayName = "ConsoleApiCall";
|
ConsoleApiCall.displayName = "ConsoleApiCall";
|
||||||
|
|
||||||
|
@ -24,11 +27,13 @@ ConsoleApiCall.propTypes = {
|
||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
sourceMapService: PropTypes.object,
|
sourceMapService: PropTypes.object,
|
||||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||||
|
open: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ConsoleApiCall(props) {
|
function ConsoleApiCall(props) {
|
||||||
const { message, sourceMapService, onViewSourceInDebugger } = props;
|
const { dispatch, message, sourceMapService, onViewSourceInDebugger, open } = props;
|
||||||
const { source, level, stacktrace, type, frame } = message;
|
const {source, level, stacktrace, type, frame } = message;
|
||||||
|
|
||||||
let messageBody;
|
let messageBody;
|
||||||
if (type === "trace") {
|
if (type === "trace") {
|
||||||
messageBody = dom.span({className: "cm-variable"}, "console.trace()");
|
messageBody = dom.span({className: "cm-variable"}, "console.trace()");
|
||||||
|
@ -41,6 +46,7 @@ function ConsoleApiCall(props) {
|
||||||
const icon = MessageIcon({level});
|
const icon = MessageIcon({level});
|
||||||
const repeat = MessageRepeat({repeat: message.repeat});
|
const repeat = MessageRepeat({repeat: message.repeat});
|
||||||
|
|
||||||
|
let collapse = "";
|
||||||
let attachment = "";
|
let attachment = "";
|
||||||
if (stacktrace) {
|
if (stacktrace) {
|
||||||
attachment = dom.div({className: "stacktrace devtools-monospace"},
|
attachment = dom.div({className: "stacktrace devtools-monospace"},
|
||||||
|
@ -49,6 +55,18 @@ function ConsoleApiCall(props) {
|
||||||
onViewSourceInDebugger: onViewSourceInDebugger
|
onViewSourceInDebugger: onViewSourceInDebugger
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
collapse = CollapseButton({
|
||||||
|
open: open,
|
||||||
|
title: l10n.getStr("messageToggleDetails"),
|
||||||
|
onClick: function () {
|
||||||
|
if (open) {
|
||||||
|
dispatch(actions.messageClose(message.id));
|
||||||
|
} else {
|
||||||
|
dispatch(actions.messageOpen(message.id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const classes = ["message", "cm-s-mozilla"];
|
const classes = ["message", "cm-s-mozilla"];
|
||||||
|
@ -61,7 +79,7 @@ function ConsoleApiCall(props) {
|
||||||
classes.push(level);
|
classes.push(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === "trace") {
|
if (open === true) {
|
||||||
classes.push("open");
|
classes.push("open");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +90,7 @@ function ConsoleApiCall(props) {
|
||||||
// @TODO add timestamp
|
// @TODO add timestamp
|
||||||
// @TODO add indent if necessary
|
// @TODO add indent if necessary
|
||||||
icon,
|
icon,
|
||||||
|
collapse,
|
||||||
dom.span({className: "message-body-wrapper"},
|
dom.span({className: "message-body-wrapper"},
|
||||||
dom.span({},
|
dom.span({},
|
||||||
dom.span({className: "message-flex-body"},
|
dom.span({className: "message-flex-body"},
|
||||||
|
|
|
@ -8,6 +8,7 @@ DIRS += [
|
||||||
]
|
]
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
'collapse-button.js',
|
||||||
'console-output.js',
|
'console-output.js',
|
||||||
'filter-bar.js',
|
'filter-bar.js',
|
||||||
'filter-button.js',
|
'filter-button.js',
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
const actionTypes = {
|
const actionTypes = {
|
||||||
MESSAGE_ADD: "MESSAGE_ADD",
|
MESSAGE_ADD: "MESSAGE_ADD",
|
||||||
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
||||||
|
MESSAGE_OPEN: "MESSAGE_OPEN",
|
||||||
|
MESSAGE_CLOSE: "MESSAGE_CLOSE",
|
||||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||||
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
||||||
FILTERS_CLEAR: "FILTERS_CLEAR",
|
FILTERS_CLEAR: "FILTERS_CLEAR",
|
||||||
|
|
|
@ -8,26 +8,47 @@
|
||||||
const Immutable = require("devtools/client/shared/vendor/immutable");
|
const Immutable = require("devtools/client/shared/vendor/immutable");
|
||||||
const constants = require("devtools/client/webconsole/new-console-output/constants");
|
const constants = require("devtools/client/webconsole/new-console-output/constants");
|
||||||
|
|
||||||
function messages(state = Immutable.List(), action) {
|
const MessageState = Immutable.Record({
|
||||||
|
messagesById: Immutable.List(),
|
||||||
|
messagesUiById: Immutable.List(),
|
||||||
|
});
|
||||||
|
|
||||||
|
function messages(state = new MessageState(), action) {
|
||||||
|
const messagesById = state.messagesById;
|
||||||
|
const messagesUiById = state.messagesUiById;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case constants.MESSAGE_ADD:
|
case constants.MESSAGE_ADD:
|
||||||
let newMessage = action.message;
|
let newMessage = action.message;
|
||||||
|
|
||||||
if (newMessage.type === "clear") {
|
if (newMessage.type === "clear") {
|
||||||
return Immutable.List([newMessage]);
|
return state.set("messagesById", Immutable.List([newMessage]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newMessage.allowRepeating && state.size > 0) {
|
if (newMessage.allowRepeating && messagesById.size > 0) {
|
||||||
let lastMessage = state.last();
|
let lastMessage = messagesById.last();
|
||||||
if (lastMessage.repeatId === newMessage.repeatId) {
|
if (lastMessage.repeatId === newMessage.repeatId) {
|
||||||
return state.pop().push(
|
return state.withMutations(function (record) {
|
||||||
|
record.set("messagesById", messagesById.pop().push(
|
||||||
newMessage.set("repeat", lastMessage.repeat + 1)
|
newMessage.set("repeat", lastMessage.repeat + 1)
|
||||||
);
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state.push(newMessage);
|
|
||||||
|
return state.withMutations(function (record) {
|
||||||
|
record.set("messagesById", messagesById.push(newMessage));
|
||||||
|
if (newMessage.type === "trace") {
|
||||||
|
record.set("messagesUiById", messagesUiById.push(newMessage.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
case constants.MESSAGES_CLEAR:
|
case constants.MESSAGES_CLEAR:
|
||||||
return Immutable.List();
|
return state.set("messagesById", Immutable.List());
|
||||||
|
case constants.MESSAGE_OPEN:
|
||||||
|
return state.set("messagesUiById", messagesUiById.push(action.id));
|
||||||
|
case constants.MESSAGE_CLOSE:
|
||||||
|
let index = state.messagesUiById.indexOf(action.id);
|
||||||
|
return state.deleteIn(["messagesUiById", index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -12,7 +12,7 @@ const {
|
||||||
} = require("devtools/client/webconsole/new-console-output/constants");
|
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||||
|
|
||||||
function getAllMessages(state) {
|
function getAllMessages(state) {
|
||||||
let messages = state.messages;
|
let messages = state.messages.messagesById;
|
||||||
let logLimit = getLogLimit(state);
|
let logLimit = getLogLimit(state);
|
||||||
let filters = getAllFilters(state);
|
let filters = getAllFilters(state);
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ function getAllMessages(state) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAllMessagesUiById(state) {
|
||||||
|
return state.messages.messagesUiById;
|
||||||
|
}
|
||||||
|
|
||||||
function filterLevel(messages, filters) {
|
function filterLevel(messages, filters) {
|
||||||
return messages.filter((message) => {
|
return messages.filter((message) => {
|
||||||
return filters[message.level] === true
|
return filters[message.level] === true
|
||||||
|
@ -59,3 +63,4 @@ function prune(messages, logLimit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getAllMessages = getAllMessages;
|
exports.getAllMessages = getAllMessages;
|
||||||
|
exports.getAllMessagesUiById = getAllMessagesUiById;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче