# HG changeset patch # User Nicolas Chevobbe # Date 1516789300 -3600 # Node ID 9a6d7da2f9b1949d649875a1e413b76c65c14857 # Parent 9e43de631c82d16a5f5fa1e149db9ba925dd4c73 Bug 1425577 - Do not clone the state for each message of a batch; r=bgrins. Here, in the case of a MESSAGES_ADD action, we clone the state before calling addMessage. Then, in addMessage, we can mutate the cloned state. This saves a lot of time since we don't have to re-create a new state for each message. MozReview-Commit-ID: GDBFBytKp5u diff --git a/devtools/client/webconsole/new-console-output/reducers/messages.js b/devtools/client/webconsole/new-console-output/reducers/messages.js --- a/devtools/client/webconsole/new-console-output/reducers/messages.js +++ b/devtools/client/webconsole/new-console-output/reducers/messages.js @@ -51,106 +51,102 @@ const MessageState = overrides => Object removedActors: [], // Map of the form {messageId : numberOfRepeat} repeatById: {}, // Map of the form {messageId : networkInformation} // `networkInformation` holds request, response, totalTime, ... networkMessagesUpdateById: {}, }, overrides)); +function cloneState(state) { + return { + messagesById: new Map(state.messagesById), + visibleMessages: [...state.visibleMessages], + filteredMessagesCount: {...state.filteredMessagesCount}, + messagesUiById: [...state.messagesUiById], + messagesTableDataById: new Map(state.messagesTableDataById), + groupsById: new Map(state.groupsById), + currentGroup: state.currentGroup, + removedActors: [...state.removedActors], + repeatById: {...state.repeatById}, + networkMessagesUpdateById: {...state.networkMessagesUpdateById}, + }; +} + function addMessage(state, filtersState, prefsState, newMessage) { const { messagesById, - messagesUiById, groupsById, currentGroup, repeatById, - visibleMessages, - filteredMessagesCount, - networkMessagesUpdateById, } = state; if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) { // When the message has a NULL type, we don't add it. return state; } if (newMessage.type === constants.MESSAGE_TYPE.END_GROUP) { // Compute the new current group. - return { - ...state, - currentGroup: getNewCurrentGroup(currentGroup, groupsById) - }; + state.currentGroup = getNewCurrentGroup(currentGroup, groupsById); + return state; } if (newMessage.allowRepeating && messagesById.size > 0) { let lastMessage = [...messagesById.values()][messagesById.size - 1]; if ( lastMessage.repeatId === newMessage.repeatId && lastMessage.groupId === currentGroup ) { - return { - ...state, - repeatById: { - ...repeatById, - [lastMessage.id]: (repeatById[lastMessage.id] || 1) + 1 - } - }; + state.repeatById[lastMessage.id] = (repeatById[lastMessage.id] || 1) + 1; + return state; } } - let newState = {...state}; // Add the new message with a reference to the parent group. let parentGroups = getParentGroups(currentGroup, groupsById); newMessage.groupId = currentGroup; newMessage.indent = parentGroups.length; const addedMessage = Object.freeze(newMessage); - newState.messagesById = (new Map(newState.messagesById)) - .set(newMessage.id, addedMessage); + state.messagesById.set(newMessage.id, addedMessage); if (newMessage.type === "trace") { // We want the stacktrace to be open by default. - newState.messagesUiById = [...messagesUiById, newMessage.id]; + state.messagesUiById.push(newMessage.id); } else if (isGroupType(newMessage.type)) { - newState.currentGroup = newMessage.id; - newState.groupsById = (new Map(newState.groupsById)) - .set(newMessage.id, parentGroups); + state.currentGroup = newMessage.id; + state.groupsById.set(newMessage.id, parentGroups); if (newMessage.type === constants.MESSAGE_TYPE.START_GROUP) { // We want the group to be open by default. - newState.messagesUiById = [...messagesUiById, newMessage.id]; + state.messagesUiById.push(newMessage.id); } } const { visible, cause - } = getMessageVisibility(addedMessage, newState, filtersState); + } = getMessageVisibility(addedMessage, state, filtersState); if (visible) { - newState.visibleMessages = [...visibleMessages, newMessage.id]; + state.visibleMessages.push(newMessage.id); } else if (DEFAULT_FILTERS.includes(cause)) { - newState.filteredMessagesCount = { - ...filteredMessagesCount, - global: filteredMessagesCount.global + 1, - [cause]: filteredMessagesCount[cause] + 1 - }; + state.filteredMessagesCount.global++; + state.filteredMessagesCount[cause]++; } // Append received network-data also into networkMessagesUpdateById // that is responsible for collecting (lazy loaded) HTTP payload data. if (newMessage.source == "network") { - newState.networkMessagesUpdateById = Object.assign({}, networkMessagesUpdateById, { - [newMessage.actor]: newMessage - }); + state.networkMessagesUpdateById[newMessage.actor] = newMessage; } - return newState; + return state; } function messages(state = MessageState(), action, filtersState, prefsState) { const { messagesById, messagesUiById, messagesTableDataById, networkMessagesUpdateById, @@ -158,18 +154,16 @@ function messages(state = MessageState() visibleMessages, } = state; const {logLimit} = prefsState; let newState; switch (action.type) { case constants.MESSAGES_ADD: - newState = state; - // Preemptively remove messages that will never be rendered let list = []; let prunableCount = 0; let lastMessageRepeatId = -1; for (let i = action.messages.length - 1; i >= 0; i--) { let message = action.messages[i]; if ( !message.groupId && !isGroupType(message.type) && @@ -186,16 +180,17 @@ function messages(state = MessageState() break; } } else { list.unshift(message); } lastMessageRepeatId = message.repeatId; } + newState = cloneState(state); list.forEach(message => { newState = addMessage(newState, filtersState, prefsState, message); }); return limitTopLevelMessageCount(newState, logLimit); case constants.MESSAGES_CLEAR: return MessageState({