--- a/devtools/client/webconsole/new-console-output/actions/messages.js
+++ b/devtools/client/webconsole/new-console-output/actions/messages.js
@@ -16,16 +16,17 @@ const {
MESSAGE_ADD,
NETWORK_MESSAGE_UPDATE,
MESSAGES_CLEAR,
MESSAGE_OPEN,
MESSAGE_CLOSE,
MESSAGE_TYPE,
MESSAGE_TABLE_RECEIVE,
MESSAGE_OBJECT_PROPERTIES_RECEIVE,
+ MESSAGE_OBJECT_ENTRIES_RECEIVE,
} = require("../constants");
const defaultIdGenerator = new IdGenerator();
function messageAdd(packet, idGenerator = null) {
if (idGenerator == null) {
idGenerator = defaultIdGenerator;
}
@@ -121,16 +122,28 @@ function networkMessageUpdate(packet, id
*/
function messageObjectPropertiesLoad(id, client, grip) {
return async (dispatch) => {
const response = await client.getPrototypeAndProperties();
dispatch(messageObjectPropertiesReceive(id, grip.actor, response));
};
}
+function messageObjectEntriesLoad(id, client, grip) {
+ return (dispatch) => {
+ client.enumEntries(enumResponse => {
+ const {iterator} = enumResponse;
+ iterator.slice(0, iterator.count, sliceResponse => {
+ console.log("sliceResponse", sliceResponse);
+ dispatch(messageObjectEntriesReceive(id, grip.actor, sliceResponse));
+ });
+ });
+ }
+}
+
/**
* This action is dispatched when properties of a grip are loaded.
*
* @param {string} id - The message id the grip is in.
* @param {string} actor - The actor id of the grip the properties were loaded from.
* @param {object} properties - A RDP packet that contains the properties of the grip.
* @returns {object}
*/
@@ -138,21 +151,41 @@ function messageObjectPropertiesReceive(
return {
type: MESSAGE_OBJECT_PROPERTIES_RECEIVE,
id,
actor,
properties
};
}
+/**
+ * This action is dispatched when entries of a grip are loaded.
+ *
+ * @param {string} id - The message id the grip is in.
+ * @param {string} actor - The actor id of the grip the properties were loaded from.
+ * @param {object} entries - A RDP packet that contains the entries of the grip.
+ * @returns {object}
+ */
+function messageObjectEntriesReceive(id, actor, entries) {
+console.log("messageObjectEntriesReceive", entries);
+ return {
+ type: MESSAGE_OBJECT_ENTRIES_RECEIVE,
+ id,
+ actor,
+ entries
+ };
+}
+
module.exports = {
messageAdd,
messagesClear,
messageOpen,
messageClose,
messageTableDataGet,
networkMessageUpdate,
messageObjectPropertiesLoad,
+ messageObjectEntriesLoad,
// for test purpose only.
messageTableDataReceive,
messageObjectPropertiesReceive,
+ messageObjectEntriesReceive,
};
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -11,16 +11,17 @@ const {
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const {
getAllMessagesById,
getAllMessagesUiById,
getAllMessagesTableDataById,
getAllMessagesObjectPropertiesById,
+ getAllMessagesObjectEntriesById,
getAllNetworkMessagesUpdateById,
getVisibleMessages,
getAllRepeatById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
const ConsoleOutput = createClass({
@@ -33,16 +34,17 @@ const ConsoleOutput = createClass({
attachRefToHud: PropTypes.func.isRequired,
openContextMenu: PropTypes.func.isRequired,
sourceMapService: PropTypes.object,
}),
dispatch: PropTypes.func.isRequired,
timestampsVisible: PropTypes.bool,
messagesTableData: PropTypes.object.isRequired,
messagesObjectProperties: PropTypes.object.isRequired,
+ messagesObjectEntries: PropTypes.object.isRequired,
messagesRepeat: PropTypes.object.isRequired,
networkMessagesUpdate: PropTypes.object.isRequired,
visibleMessages: PropTypes.array.isRequired,
},
componentDidMount() {
// Do the scrolling in the nextTick since this could hit console startup performances.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1355869
@@ -80,16 +82,17 @@ const ConsoleOutput = createClass({
render() {
let {
dispatch,
visibleMessages,
messages,
messagesUi,
messagesTableData,
messagesObjectProperties,
+ messagesObjectEntries,
messagesRepeat,
networkMessagesUpdate,
serviceContainer,
timestampsVisible,
} = this.props;
let messageNodes = visibleMessages.map((messageId) => MessageContainer({
dispatch,
@@ -98,16 +101,17 @@ const ConsoleOutput = createClass({
serviceContainer,
open: messagesUi.includes(messageId),
tableData: messagesTableData.get(messageId),
timestampsVisible,
repeat: messagesRepeat[messageId],
networkMessageUpdate: networkMessagesUpdate[messageId],
getMessage: () => messages.get(messageId),
loadedObjectProperties: messagesObjectProperties.get(messageId),
+ loadedObjectEntries: messagesObjectEntries.get(messageId),
}));
return (
dom.div({
className: "webconsole-output",
onContextMenu: this.onContextMenu,
ref: node => {
this.outputNode = node;
@@ -131,15 +135,16 @@ function isScrolledToBottom(outputNode,
function mapStateToProps(state, props) {
return {
messages: getAllMessagesById(state),
visibleMessages: getVisibleMessages(state),
messagesUi: getAllMessagesUiById(state),
messagesTableData: getAllMessagesTableDataById(state),
messagesObjectProperties: getAllMessagesObjectPropertiesById(state),
+ messagesObjectEntries: getAllMessagesObjectEntriesById(state),
messagesRepeat: getAllRepeatById(state),
networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
timestampsVisible: state.ui.timestampsVisible,
};
}
module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -40,16 +40,17 @@ GripMessageBody.propTypes = {
serviceContainer: PropTypes.shape({
createElement: PropTypes.func.isRequired,
hudProxyClient: PropTypes.object.isRequired,
}),
userProvidedStyle: PropTypes.string,
useQuotes: PropTypes.bool,
escapeWhitespace: PropTypes.bool,
loadedObjectProperties: PropTypes.object,
+ loadedObjectEntries: PropTypes.object,
type: PropTypes.string,
helperType: PropTypes.string,
};
GripMessageBody.defaultProps = {
mode: MODE.LONG,
};
@@ -59,16 +60,17 @@ function GripMessageBody(props) {
messageId,
grip,
userProvidedStyle,
serviceContainer,
useQuotes,
escapeWhitespace,
mode = MODE.LONG,
loadedObjectProperties,
+ loadedObjectEntries,
} = props;
let styleObject;
if (userProvidedStyle && userProvidedStyle !== "") {
styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
}
let onDOMNodeMouseOver;
@@ -101,16 +103,21 @@ function GripMessageBody(props) {
value: grip
}
}],
getObjectProperties: actor => loadedObjectProperties && loadedObjectProperties[actor],
loadObjectProperties: object => {
const client = new ObjectClient(serviceContainer.hudProxyClient, object);
dispatch(actions.messageObjectPropertiesLoad(messageId, client, object));
},
+ getObjectEntries: actor => loadedObjectEntries && loadedObjectEntries[actor],
+ loadObjectEntries: object => {
+ const client = new ObjectClient(serviceContainer.hudProxyClient, object);
+ dispatch(actions.messageObjectEntriesLoad(messageId, client, object));
+ },
};
if (typeof grip === "string" || grip.type === "longString") {
Object.assign(objectInspectorProps, {
useQuotes,
escapeWhitespace,
style: styleObject
});
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -34,16 +34,17 @@ const MessageContainer = createClass({
open: PropTypes.bool.isRequired,
serviceContainer: PropTypes.object.isRequired,
tableData: PropTypes.object,
timestampsVisible: PropTypes.bool.isRequired,
repeat: PropTypes.number,
networkMessageUpdate: PropTypes.object,
getMessage: PropTypes.func.isRequired,
loadedObjectProperties: PropTypes.object,
+ loadedObjectEntries: PropTypes.object,
},
getDefaultProps: function () {
return {
open: false,
};
},
@@ -52,23 +53,26 @@ const MessageContainer = createClass({
const openChanged = this.props.open !== nextProps.open;
const tableDataChanged = this.props.tableData !== nextProps.tableData;
const timestampVisibleChanged =
this.props.timestampsVisible !== nextProps.timestampsVisible;
const networkMessageUpdateChanged =
this.props.networkMessageUpdate !== nextProps.networkMessageUpdate;
const loadedObjectPropertiesChanged =
this.props.loadedObjectProperties !== nextProps.loadedObjectProperties;
+ const loadedObjectEntriesChanged =
+ this.props.loadedObjectEntries !== nextProps.loadedObjectEntries;
return repeatChanged
|| openChanged
|| tableDataChanged
|| timestampVisibleChanged
|| networkMessageUpdateChanged
- || loadedObjectPropertiesChanged;
+ || loadedObjectPropertiesChanged
+ || loadedObjectEntriesChanged;
},
render() {
const message = this.props.getMessage();
let MessageComponent = getMessageComponent(message);
return MessageComponent(Object.assign({message}, this.props));
}
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
@@ -22,32 +22,34 @@ ConsoleApiCall.displayName = "ConsoleApi
ConsoleApiCall.propTypes = {
dispatch: PropTypes.func.isRequired,
message: PropTypes.object.isRequired,
open: PropTypes.bool,
serviceContainer: PropTypes.object.isRequired,
timestampsVisible: PropTypes.bool.isRequired,
loadedObjectProperties: PropTypes.object,
+ loadedObjectEntries: PropTypes.object,
};
ConsoleApiCall.defaultProps = {
open: false,
};
function ConsoleApiCall(props) {
const {
dispatch,
message,
open,
tableData,
serviceContainer,
timestampsVisible,
repeat,
loadedObjectProperties,
+ loadedObjectEntries,
} = props;
const {
id: messageId,
indent,
source,
type,
level,
stacktrace,
@@ -57,16 +59,17 @@ function ConsoleApiCall(props) {
messageText,
userProvidedStyles,
} = message;
let messageBody;
const messageBodyConfig = {
dispatch,
loadedObjectProperties,
+ loadedObjectEntries,
messageId,
parameters,
userProvidedStyles,
serviceContainer,
type,
};
if (type === "trace") {
@@ -124,16 +127,17 @@ function ConsoleApiCall(props) {
timestampsVisible,
});
}
function formatReps(options = {}) {
const {
dispatch,
loadedObjectProperties,
+ loadedObjectEntries,
messageId,
parameters,
serviceContainer,
userProvidedStyles,
type,
} = options;
return (
@@ -143,16 +147,17 @@ function formatReps(options = {}) {
dispatch,
messageId,
grip,
key,
userProvidedStyle: userProvidedStyles ? userProvidedStyles[key] : null,
serviceContainer,
useQuotes: false,
loadedObjectProperties,
+ loadedObjectEntries,
type,
}))
// Interleave spaces.
.reduce((arr, v, i) => {
return i + 1 < parameters.length
? arr.concat(v, dom.span({}, " "))
: arr.concat(v);
}, [])
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -17,25 +17,27 @@ const GripMessageBody = require("devtool
EvaluationResult.displayName = "EvaluationResult";
EvaluationResult.propTypes = {
dispatch: PropTypes.func.isRequired,
message: PropTypes.object.isRequired,
timestampsVisible: PropTypes.bool.isRequired,
serviceContainer: PropTypes.object,
loadedObjectProperties: PropTypes.object,
+ loadedObjectEntries: PropTypes.object,
};
function EvaluationResult(props) {
const {
dispatch,
message,
serviceContainer,
timestampsVisible,
loadedObjectProperties,
+ loadedObjectEntries,
} = props;
const {
source,
type,
helperType,
level,
id: messageId,
@@ -61,16 +63,17 @@ function EvaluationResult(props) {
messageBody = GripMessageBody({
dispatch,
messageId,
grip: parameters,
serviceContainer,
useQuotes: true,
escapeWhitespace: false,
loadedObjectProperties,
+ loadedObjectEntries,
type,
helperType,
});
}
const topLevelClasses = ["cm-s-mozilla"];
return Message({
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -9,16 +9,17 @@ const actionTypes = {
BATCH_ACTIONS: "BATCH_ACTIONS",
MESSAGE_ADD: "MESSAGE_ADD",
MESSAGES_CLEAR: "MESSAGES_CLEAR",
MESSAGE_OPEN: "MESSAGE_OPEN",
MESSAGE_CLOSE: "MESSAGE_CLOSE",
NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
MESSAGE_OBJECT_PROPERTIES_RECEIVE: "MESSAGE_OBJECT_PROPERTIES_RECEIVE",
+ MESSAGE_OBJECT_ENTRIES_RECEIVE: "MESSAGE_OBJECT_ENTRIES_RECEIVE",
REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
FILTER_TOGGLE: "FILTER_TOGGLE",
FILTER_TEXT_SET: "FILTER_TEXT_SET",
FILTERS_CLEAR: "FILTERS_CLEAR",
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
};
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -27,16 +27,21 @@ const MessageState = Immutable.Record({
// Map of the form {messageId : tableData}, which represent the data passed
// as an argument in console.table calls.
messagesTableDataById: Immutable.Map(),
// Map of the form {messageId : {[actor]: properties}}, where `properties` is
// a RDP packet containing the properties of the ${actor} grip.
// This map is consumed by the ObjectInspector so we only load properties once,
// when needed (when an ObjectInspector node is expanded), and then caches them.
messagesObjectPropertiesById: Immutable.Map(),
+ // Map of the form {messageId : {[actor]: entries}}, where `entries` is
+ // a RDP packet containing the entries of the ${actor} grip.
+ // This map is consumed by the ObjectInspector so we only load entries once,
+ // when needed (when an ObjectInspector node is expanded), and then caches them.
+ messagesObjectEntriesById: Immutable.Map(),
// Map of the form {groupMessageId : groupArray},
// where groupArray is the list of of all the parent groups' ids of the groupMessageId.
groupsById: Immutable.Map(),
// Message id of the current group (no corresponding console.groupEnd yet).
currentGroup: null,
// Array of removed actors (i.e. actors logged in removed messages) we keep track of
// in order to properly release them.
// This array is not supposed to be consumed by any UI component.
@@ -49,16 +54,17 @@ const MessageState = Immutable.Record({
});
function messages(state = new MessageState(), action, filtersState, prefsState) {
const {
messagesById,
messagesUiById,
messagesTableDataById,
messagesObjectPropertiesById,
+ messagesObjectEntriesById,
networkMessagesUpdateById,
groupsById,
currentGroup,
repeatById,
visibleMessages,
} = state;
const {logLimit} = prefsState;
@@ -202,16 +208,26 @@ function messages(state = new MessageSta
"messagesObjectPropertiesById",
messagesObjectPropertiesById.set(
action.id,
Object.assign({
[action.actor]: action.properties
}, messagesObjectPropertiesById.get(action.id))
)
);
+ case constants.MESSAGE_OBJECT_ENTRIES_RECEIVE:
+ return state.set(
+ "messagesObjectEntriesById",
+ messagesObjectEntriesById.set(
+ action.id,
+ Object.assign({
+ [action.actor]: action.entries
+ }, messagesObjectEntriesById.get(action.id))
+ )
+ );
case constants.NETWORK_MESSAGE_UPDATE:
return state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.message.id]: action.message
})
);
@@ -351,16 +367,20 @@ function limitTopLevelMessageCount(state
}
if (mapHasRemovedIdKey(record.groupsById)) {
record.set("groupsById", record.groupsById.withMutations(cleanUpCollection));
}
if (mapHasRemovedIdKey(record.messagesObjectPropertiesById)) {
record.set("messagesObjectPropertiesById",
record.messagesObjectPropertiesById.withMutations(cleanUpCollection));
}
+ if (mapHasRemovedIdKey(record.messagesObjectEntriesById)) {
+ record.set("messagesObjectEntriesById",
+ record.messagesObjectEntriesById.withMutations(cleanUpCollection));
+ }
if (objectHasRemovedIdKey(record.repeatById)) {
record.set("repeatById", cleanUpObject(record.repeatById));
}
if (objectHasRemovedIdKey(record.networkMessagesUpdateById)) {
record.set("networkMessagesUpdateById",
cleanUpObject(record.networkMessagesUpdateById));
}
@@ -390,16 +410,21 @@ function getAllActorsInMessage(message,
return res;
}, [])];
const loadedProperties = state.messagesObjectPropertiesById.get(message.id);
if (loadedProperties) {
actors.push(...Object.keys(loadedProperties));
}
+ const loadedEntries = state.messagesObjectEntriesById.get(message.id);
+ if (loadedEntries) {
+ actors.push(...Object.keys(loadedEntries));
+ }
+
return actors;
}
/**
* Returns total count of top level messages (those which are not
* within a group).
*/
function getToplevelMessageCount(record) {
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -20,16 +20,20 @@ function getAllMessagesUiById(state) {
function getAllMessagesTableDataById(state) {
return state.messages.messagesTableDataById;
}
function getAllMessagesObjectPropertiesById(state) {
return state.messages.messagesObjectPropertiesById;
}
+function getAllMessagesObjectEntriesById(state) {
+ return state.messages.messagesObjectEntriesById;
+}
+
function getAllGroupsById(state) {
return state.messages.groupsById;
}
function getCurrentGroup(state) {
return state.messages.currentGroup;
}
@@ -51,9 +55,10 @@ module.exports = {
getAllMessagesUiById,
getAllMessagesTableDataById,
getAllGroupsById,
getCurrentGroup,
getVisibleMessages,
getAllRepeatById,
getAllNetworkMessagesUpdateById,
getAllMessagesObjectPropertiesById,
+ getAllMessagesObjectEntriesById,
};
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
@@ -22,25 +22,31 @@ const consoleApiCommands = [
"console.log('myregex', /a.b.c/)",
"console.table(['red', 'green', 'blue']);",
"console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
];
let consoleApi = new Map(consoleApiCommands.map(
cmd => [cmd, {keys: [cmd], code: cmd}]));
-consoleApi.set("console.map('mymap')", {
- keys: ["console.map('mymap')"],
+consoleApi.set("console.log('mymap')", {
+ keys: ["console.log('mymap')"],
code: `
var map = new Map();
map.set("key1", "value1");
map.set("key2", "value2");
console.log('mymap', map);
`});
+consoleApi.set("console.log('myset')", {
+ keys: ["console.log('myset')"],
+ code: `
+console.log('myset', new Set(["a", "b"]));
+`});
+
consoleApi.set("console.trace()", {
keys: ["console.trace()"],
code: `
function testStacktraceFiltering() {
console.trace()
}
function foo() {
testStacktraceFiltering()
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -553,30 +553,30 @@ stubPreparedMessages.set("console.log('m
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
-stubPreparedMessages.set("console.map('mymap')", new ConsoleMessage({
+stubPreparedMessages.set("console.log('mymap')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
- "timeStamp": 1493125410207,
+ "timeStamp": 1501506737042,
"type": "log",
"helperType": null,
"level": "log",
"messageText": null,
"parameters": [
"mymap",
{
"type": "object",
- "actor": "server1.conn0.child1/obj36",
+ "actor": "server1.conn0.child1/obj37",
"class": "Map",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "MapLike",
"size": 2,
@@ -588,30 +588,73 @@ stubPreparedMessages.set("console.map('m
[
"key2",
"value2"
]
]
}
}
],
- "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
+ "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj37\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 5,
"column": 1
},
"groupId": null,
"exceptionDocURL": null,
"userProvidedStyles": [],
"notes": null,
"indent": 0
}));
+stubPreparedMessages.set("console.log('myset')", new ConsoleMessage({
+ "id": "1",
+ "allowRepeating": true,
+ "source": "console-api",
+ "timeStamp": 1501506737051,
+ "type": "log",
+ "helperType": null,
+ "level": "log",
+ "messageText": null,
+ "parameters": [
+ "myset",
+ {
+ "type": "object",
+ "actor": "server1.conn0.child1/obj38",
+ "class": "Set",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 2,
+ "items": [
+ "a",
+ "b"
+ ]
+ }
+ }
+ ],
+ "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myset\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj38\",\"class\":\"Set\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"ArrayLike\",\"length\":2,\"items\":[\"a\",\"b\"]}}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[]}",
+ "stacktrace": null,
+ "frame": {
+ "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+ "line": 2,
+ "column": 1
+ },
+ "groupId": null,
+ "exceptionDocURL": null,
+ "userProvidedStyles": [],
+ "notes": null,
+ "indent": 0
+}));
+
stubPreparedMessages.set("console.trace()", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"timeStamp": 1479159910198,
"type": "trace",
"helperType": null,
"level": "log",
@@ -1684,26 +1727,26 @@ stubPackets.set("console.log('myobject',
"styles": [],
"timeStamp": 1493125748177,
"timer": null,
"workerType": "none",
"category": "webdev"
}
});
-stubPackets.set("console.map('mymap')", {
+stubPackets.set("console.log('mymap')", {
"from": "server1.conn0.child1/consoleActor2",
"type": "consoleAPICall",
"message": {
"addonId": "",
"arguments": [
"mymap",
{
"type": "object",
- "actor": "server1.conn0.child1/obj36",
+ "actor": "server1.conn0.child1/obj37",
"class": "Map",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "MapLike",
"size": 2,
@@ -1724,17 +1767,58 @@ stubPackets.set("console.map('mymap')",
"counter": null,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"functionName": "triggerPacket",
"groupName": "",
"level": "log",
"lineNumber": 5,
"private": false,
"styles": [],
- "timeStamp": 1493125410207,
+ "timeStamp": 1501506737042,
+ "timer": null,
+ "workerType": "none",
+ "category": "webdev"
+ }
+});
+
+stubPackets.set("console.log('myset')", {
+ "from": "server1.conn0.child1/consoleActor2",
+ "type": "consoleAPICall",
+ "message": {
+ "addonId": "",
+ "arguments": [
+ "myset",
+ {
+ "type": "object",
+ "actor": "server1.conn0.child1/obj38",
+ "class": "Set",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "ArrayLike",
+ "length": 2,
+ "items": [
+ "a",
+ "b"
+ ]
+ }
+ }
+ ],
+ "columnNumber": 1,
+ "counter": null,
+ "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+ "functionName": "triggerPacket",
+ "groupName": "",
+ "level": "log",
+ "lineNumber": 2,
+ "private": false,
+ "styles": [],
+ "timeStamp": 1501506737051,
"timer": null,
"workerType": "none",
"category": "webdev"
}
});
stubPackets.set("console.trace()", {
"from": "server1.conn12.child1/consoleActor2",
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -38,15 +38,16 @@ skip-if = (os == 'linux' && bits == 32 &
[browser_webconsole_input_focus.js]
[browser_webconsole_keyboard_accessibility.js]
[browser_webconsole_location_debugger_link.js]
[browser_webconsole_location_scratchpad_link.js]
[browser_webconsole_location_styleeditor_link.js]
[browser_webconsole_network_messages_click.js]
[browser_webconsole_nodes_highlight.js]
[browser_webconsole_nodes_select.js]
+[browser_webconsole_object_inspector_entries.js]
[browser_webconsole_object_inspector.js]
[browser_webconsole_observer_notifications.js]
[browser_webconsole_shows_reqs_in_netmonitor.js]
[browser_webconsole_stacktrace_location_debugger_link.js]
[browser_webconsole_stacktrace_location_scratchpad_link.js]
[browser_webconsole_string.js]
[browser_webconsole_timestamps.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_entries.js
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check expanding/collapsing object inspector in the console.
+const TEST_URI = "data:text/html;charset=utf8,<h1>test Object Inspector</h1>";
+
+add_task(async function () {
+ let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+ let hud = toolbox.getCurrentPanel().hud;
+
+ const store = hud.ui.newConsoleOutput.getStore();
+ // Adding logging each time the store is modified in order to check
+ // the store state in case of failure.
+ store.subscribe(() => {
+ const messages = store.getState().messages.messagesById
+ .reduce(function (res, {id, type, parameters, messageText}) {
+ res.push({id, type, parameters, messageText});
+ return res;
+ }, []);
+ info("messages : " + JSON.stringify(messages));
+ });
+
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
+ content.wrappedJSObject.console.log(
+ "oi-entries-test",
+ new Map([["a", 42], ["b", 10]]),
+ new Map(
+ Array.from({length: 20})
+ .map((el, i) => [String.fromCharCode(65 + i), i + 1])
+ ),
+ );
+ });
+
+ let node = await waitFor(() => findMessage(hud, "oi-entries-test"));
+ const objectInspectors = [...node.querySelectorAll(".tree")];
+ is(objectInspectors.length, 2, "There is the expected number of object inspectors");
+
+ const [smallMapOi, largeMapOi] = objectInspectors;
+
+ info("Expanding the small map");
+ let onMapOiMutation = waitForNodeMutation(smallMapOi, {
+ childList: true
+ });
+
+ smallMapOi.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(smallMapOi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let smallMapOiNodes = smallMapOi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(smallMapOiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the map");
+ let entriesObject = smallMapOiNodes[2];
+ is(entriesObject.textContent, "<entries>", "There is the expected <entries> node");
+ onMapOiMutation = waitForNodeMutation(smallMapOi, {
+ childList: true
+ });
+
+ entriesObject.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(entriesObject.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ smallMapOiNodes = smallMapOi.querySelectorAll(".node");
+ // There are now 6 nodes, the 4 original ones, and the 2 entries.
+ is(smallMapOiNodes.length, 6, "There is the expected number of nodes in the tree");
+
+ info("Expanding the large map");
+ onMapOiMutation = waitForNodeMutation(largeMapOi, {
+ childList: true
+ });
+
+ largeMapOi.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(largeMapOi.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ let largeMapOiNodes = largeMapOi.querySelectorAll(".node");
+ // There are 4 nodes: the root, size, entries and the proto.
+ is(largeMapOiNodes.length, 4, "There is the expected number of nodes in the tree");
+
+ info("Expanding the <entries> leaf of the map");
+ entriesObject = largeMapOiNodes[2];
+ is(entriesObject.textContent, "<entries>", "There is the expected <entries> node");
+ onMapOiMutation = waitForNodeMutation(largeMapOi, {
+ childList: true
+ });
+
+ entriesObject.querySelector(".arrow").click();
+ await onMapOiMutation;
+
+ ok(entriesObject.querySelector(".arrow").classList.contains("expanded"),
+ "The arrow of the node has the expected class after clicking on it");
+
+ largeMapOiNodes = largeMapOi.querySelectorAll(".node");
+ // There are now 24 nodes, the 4 original ones, and the 20 entries.
+ is(largeMapOiNodes.length, 24, "There is the expected number of nodes in the tree");
+});
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -7,16 +7,17 @@ const {
getAllMessagesById,
getAllMessagesTableDataById,
getAllMessagesUiById,
getAllNetworkMessagesUpdateById,
getAllRepeatById,
getCurrentGroup,
getVisibleMessages,
getAllMessagesObjectPropertiesById,
+ getAllMessagesObjectEntriesById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {
clonePacket,
getMessageAt,
setupActions,
setupStore,
} = require("devtools/client/webconsole/new-console-output/test/helpers");
const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
@@ -864,9 +865,105 @@ describe("Message reducer:", () => {
});
// This addition will remove the second table message.
dispatch(actions.messageAdd(stubPackets.get("console.log('foobar', 'test')")));
expect(getAllMessagesObjectPropertiesById(getState()).size).toBe(0);
});
});
+
+ describe("messagesObjectEntriesById", () => {
+ it(`adds messagesObjectEntriesById data in response to
+ MESSAGE_OBJECT_ENTRIES_RECEIVE action`, () => {
+ const { dispatch, getState } = setupStore([]);
+
+ // Add 2 log messages with loaded entries.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('myset')")));
+ dispatch(actions.messageAdd(stubPackets.get("console.log('mymap')")));
+
+ let messages = getAllMessagesById(getState());
+
+ const setEntries = Symbol();
+ const mapEntries = Symbol();
+ const mapEntries2 = Symbol();
+
+ const [id1, id2] = [...messages.keys()];
+ dispatch(actions.messageObjectEntriesReceive(id1, "fakeActor1", setEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor2", mapEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor3", mapEntries2));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(2);
+
+ expect(loadedEntries.get(id1)).toEqual({
+ fakeActor1: setEntries,
+ });
+
+ expect(loadedEntries.get(id2)).toEqual({
+ fakeActor2: mapEntries,
+ fakeActor3: mapEntries2,
+ });
+ });
+
+ it("resets messagesObjectEntriesById in response to MESSAGES_CLEAR action", () => {
+ const { dispatch, getState } = setupStore([
+ "console.log('myset')"
+ ]);
+
+ let messages = getAllMessagesById(getState());
+ const entries = Symbol("entries");
+ const message = messages.first();
+ const {actor} = message.parameters[1];
+
+ dispatch(actions.messageObjectEntriesReceive(message.id, actor, entries));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(1);
+ expect(loadedEntries.get(message.id)).toEqual({
+ [actor]: entries
+ });
+
+ dispatch(actions.messagesClear());
+
+ expect(getAllMessagesObjectEntriesById(getState()).size).toBe(0);
+ });
+
+ it("cleans messagesObjectPropertiesById when messages are pruned", () => {
+ const { dispatch, getState } = setupStore([], null, {
+ logLimit: 2
+ });
+
+ // Add 2 log messages with loaded entries.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('myset')")));
+ dispatch(actions.messageAdd(stubPackets.get("console.log('mymap')")));
+
+ let messages = getAllMessagesById(getState());
+
+ const setEntries = Symbol();
+ const mapEntries = Symbol();
+ const mapEntries2 = Symbol();
+
+ const [id1, id2] = [...messages.keys()];
+ dispatch(actions.messageObjectEntriesReceive(id1, "fakeActor1", setEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor2", mapEntries));
+ dispatch(actions.messageObjectEntriesReceive(id2, "fakeActor3", mapEntries2));
+
+ let loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(2);
+
+ // This addition will remove the first message.
+ dispatch(actions.messageAdd(stubPackets.get("console.log(undefined)")));
+
+ loadedEntries = getAllMessagesObjectEntriesById(getState());
+ expect(loadedEntries.size).toBe(1);
+ expect(loadedEntries.get(id2)).toEqual({
+ fakeActor2: mapEntries,
+ fakeActor3: mapEntries2,
+ });
+
+ // This addition will remove the second table message.
+ dispatch(actions.messageAdd(stubPackets.get("console.log('foobar', 'test')")));
+
+ expect(getAllMessagesObjectEntriesById(getState()).size).toBe(0);
+ });
+ });
});
--- a/devtools/client/webconsole/new-console-output/test/store/release-actors.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/release-actors.test.js
@@ -80,29 +80,37 @@ describe("Release actor enhancer:", () =
dispatch(actions.messageAdd(
stubPackets.get("console.log('myarray', ['red', 'green', 'blue'])")));
let messages = getAllMessagesById(getState());
const firstMessage = messages.first();
const firstMessageActor = firstMessage.parameters[1].actor;
const arrayProperties = Symbol();
const arraySubProperties = Symbol();
+ const mapEntries1 = Symbol();
+ const mapEntries2 = Symbol();
const [id] = [...messages.keys()];
dispatch(actions.messageObjectPropertiesReceive(
id, "fakeActor1", arrayProperties));
dispatch(actions.messageObjectPropertiesReceive(
id, "fakeActor2", arraySubProperties));
+ dispatch(actions.messageObjectEntriesReceive(
+ id, "mapActor1", mapEntries1));
+ dispatch(actions.messageObjectEntriesReceive(
+ id, "mapActor2", mapEntries2));
const packet = clonePacket(stubPackets.get(
"console.assert(false, {message: 'foobar'})"));
const secondMessageActor = packet.message.arguments[0].actor;
dispatch(actions.messageAdd(packet));
dispatch(actions.messagesClear());
- expect(releasedActors.length).toBe(4);
+ expect(releasedActors.length).toBe(6);
expect(releasedActors).toInclude(firstMessageActor);
expect(releasedActors).toInclude("fakeActor1");
expect(releasedActors).toInclude("fakeActor2");
+ expect(releasedActors).toInclude("mapActor1");
+ expect(releasedActors).toInclude("mapActor2");
expect(releasedActors).toInclude(secondMessageActor);
});
});
});
--- a/devtools/client/webconsole/new-console-output/test/store/search.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/search.test.js
@@ -83,15 +83,15 @@ describe("Searching in grips", () => {
function prepareBaseStore() {
const store = setupStore([
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.table(['red', 'green', 'blue']);",
"console.count('bar')",
"console.log('myarray', ['red', 'green', 'blue'])",
"console.log('myregex', /a.b.c/)",
- "console.map('mymap')",
+ "console.log('mymap')",
"console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});",
"GET request",
]);
return store;
}