summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/reducers/messages.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/netmonitor/src/reducers/messages.js')
-rw-r--r--devtools/client/netmonitor/src/reducers/messages.js335
1 files changed, 335 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/reducers/messages.js b/devtools/client/netmonitor/src/reducers/messages.js
new file mode 100644
index 0000000000..27d7da28c7
--- /dev/null
+++ b/devtools/client/netmonitor/src/reducers/messages.js
@@ -0,0 +1,335 @@
+/* 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";
+
+const {
+ SELECT_REQUEST,
+ MSG_ADD,
+ MSG_SELECT,
+ MSG_OPEN_DETAILS,
+ MSG_CLEAR,
+ MSG_TOGGLE_FILTER_TYPE,
+ MSG_TOGGLE_CONTROL,
+ MSG_SET_FILTER_TEXT,
+ MSG_TOGGLE_COLUMN,
+ MSG_RESET_COLUMNS,
+ MSG_CLOSE_CONNECTION,
+ CHANNEL_TYPE,
+ SET_EVENT_STREAM_FLAG,
+} = require("resource://devtools/client/netmonitor/src/constants.js");
+
+/**
+ * The default column states for the MessageListItem component.
+ */
+const defaultColumnsState = {
+ data: true,
+ size: false,
+ time: true,
+};
+
+const defaultWSColumnsState = {
+ ...defaultColumnsState,
+ opCode: false,
+ maskBit: false,
+ finBit: false,
+};
+
+const defaultSSEColumnsState = {
+ ...defaultColumnsState,
+ eventName: false,
+ lastEventId: false,
+ retry: false,
+};
+
+/**
+ * Returns a new object of default cols.
+ */
+function getMessageDefaultColumnsState(channelType) {
+ let columnsState = defaultColumnsState;
+ const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
+
+ if (channelType === WEB_SOCKET) {
+ columnsState = defaultWSColumnsState;
+ } else if (channelType === EVENT_STREAM) {
+ columnsState = defaultSSEColumnsState;
+ }
+
+ return Object.assign({}, columnsState);
+}
+
+/**
+ * This structure stores list of all WebSocket and EventSource messages received
+ * from the backend.
+ */
+function Messages(initialState = {}) {
+ const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
+
+ return {
+ // Map with all requests (key = resourceId, value = array of message objects)
+ messages: new Map(),
+ messageFilterText: "",
+ // Default filter type is "all",
+ messageFilterType: "all",
+ showControlFrames: false,
+ selectedMessage: null,
+ messageDetailsOpen: false,
+ currentChannelId: null,
+ currentChannelType: null,
+ currentRequestId: null,
+ closedConnections: new Map(),
+ columns: null,
+ sseColumns: getMessageDefaultColumnsState(EVENT_STREAM),
+ wsColumns: getMessageDefaultColumnsState(WEB_SOCKET),
+ ...initialState,
+ };
+}
+
+/**
+ * When a network request is selected,
+ * set the current resourceId affiliated with the connection.
+ */
+function setCurrentChannel(state, action) {
+ if (!action.request) {
+ return state;
+ }
+
+ const { id, cause, resourceId, isEventStream } = action.request;
+ const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
+ let currentChannelType = null;
+ let columnsKey = "columns";
+ if (cause.type === "websocket") {
+ currentChannelType = WEB_SOCKET;
+ columnsKey = "wsColumns";
+ } else if (isEventStream) {
+ currentChannelType = EVENT_STREAM;
+ columnsKey = "sseColumns";
+ }
+
+ return {
+ ...state,
+ columns:
+ currentChannelType === state.currentChannelType
+ ? { ...state.columns }
+ : { ...state[columnsKey] },
+ currentChannelId: resourceId,
+ currentChannelType,
+ currentRequestId: id,
+ // Default filter text is empty string for a new connection
+ messageFilterText: "",
+ };
+}
+
+/**
+ * If the request is already selected and isEventStream flag
+ * is added later, we need to update currentChannelType & columns.
+ */
+function updateCurrentChannel(state, action) {
+ if (state.currentRequestId === action.id) {
+ const currentChannelType = CHANNEL_TYPE.EVENT_STREAM;
+ return {
+ ...state,
+ columns: { ...state.sseColumns },
+ currentChannelType,
+ };
+ }
+ return state;
+}
+
+/**
+ * Appending new message into the map.
+ */
+function addMessage(state, action) {
+ const { httpChannelId } = action;
+ const nextState = { ...state };
+
+ const newMessage = {
+ httpChannelId,
+ ...action.data,
+ };
+
+ nextState.messages = mapSet(
+ nextState.messages,
+ newMessage.httpChannelId,
+ newMessage
+ );
+
+ return nextState;
+}
+
+/**
+ * Select specific message.
+ */
+function selectMessage(state, action) {
+ return {
+ ...state,
+ selectedMessage: action.message,
+ messageDetailsOpen: action.open,
+ };
+}
+
+/**
+ * Shows/Hides the MessagePayload component.
+ */
+function openMessageDetails(state, action) {
+ return {
+ ...state,
+ messageDetailsOpen: action.open,
+ };
+}
+
+/**
+ * Clear messages of the request from the state.
+ */
+function clearMessages(state) {
+ const nextState = { ...state };
+ const defaultState = Messages();
+ nextState.messages = new Map(state.messages);
+ nextState.messages.delete(nextState.currentChannelId);
+
+ // Reset fields to default state.
+ nextState.selectedMessage = defaultState.selectedMessage;
+ nextState.messageDetailsOpen = defaultState.messageDetailsOpen;
+
+ return nextState;
+}
+
+/**
+ * Toggle the message filter type of the connection.
+ */
+function toggleMessageFilterType(state, action) {
+ return {
+ ...state,
+ messageFilterType: action.filter,
+ };
+}
+
+/**
+ * Toggle control frames for the WebSocket connection.
+ */
+function toggleControlFrames(state, action) {
+ return {
+ ...state,
+ showControlFrames: !state.showControlFrames,
+ };
+}
+
+/**
+ * Set the filter text of the current channelId.
+ */
+function setMessageFilterText(state, action) {
+ return {
+ ...state,
+ messageFilterText: action.text,
+ };
+}
+
+/**
+ * Toggle the user specified column view state.
+ */
+function toggleColumn(state, action) {
+ const { column } = action;
+ let columnsKey = null;
+ if (state.currentChannelType === CHANNEL_TYPE.WEB_SOCKET) {
+ columnsKey = "wsColumns";
+ } else {
+ columnsKey = "sseColumns";
+ }
+ const newColumnsState = {
+ ...state[columnsKey],
+ [column]: !state[columnsKey][column],
+ };
+ return {
+ ...state,
+ columns: newColumnsState,
+ [columnsKey]: newColumnsState,
+ };
+}
+
+/**
+ * Reset back to default columns view state.
+ */
+function resetColumns(state) {
+ let columnsKey = null;
+ if (state.currentChannelType === CHANNEL_TYPE.WEB_SOCKET) {
+ columnsKey = "wsColumns";
+ } else {
+ columnsKey = "sseColumns";
+ }
+ const newColumnsState = getMessageDefaultColumnsState(
+ state.currentChannelType
+ );
+ return {
+ ...state,
+ [columnsKey]: newColumnsState,
+ columns: newColumnsState,
+ };
+}
+
+function closeConnection(state, action) {
+ const { httpChannelId, code, reason } = action;
+ const nextState = { ...state };
+
+ nextState.closedConnections.set(httpChannelId, {
+ code,
+ reason,
+ });
+
+ return nextState;
+}
+
+/**
+ * Append new item into existing map and return new map.
+ */
+function mapSet(map, key, value) {
+ const newMap = new Map(map);
+ if (newMap.has(key)) {
+ const messagesArray = [...newMap.get(key)];
+ messagesArray.push(value);
+ newMap.set(key, messagesArray);
+ return newMap;
+ }
+ return newMap.set(key, [value]);
+}
+
+/**
+ * This reducer is responsible for maintaining list of
+ * messages within the Network panel.
+ */
+function messages(state = Messages(), action) {
+ switch (action.type) {
+ case SELECT_REQUEST:
+ return setCurrentChannel(state, action);
+ case SET_EVENT_STREAM_FLAG:
+ return updateCurrentChannel(state, action);
+ case MSG_ADD:
+ return addMessage(state, action);
+ case MSG_SELECT:
+ return selectMessage(state, action);
+ case MSG_OPEN_DETAILS:
+ return openMessageDetails(state, action);
+ case MSG_CLEAR:
+ return clearMessages(state);
+ case MSG_TOGGLE_FILTER_TYPE:
+ return toggleMessageFilterType(state, action);
+ case MSG_TOGGLE_CONTROL:
+ return toggleControlFrames(state, action);
+ case MSG_SET_FILTER_TEXT:
+ return setMessageFilterText(state, action);
+ case MSG_TOGGLE_COLUMN:
+ return toggleColumn(state, action);
+ case MSG_RESET_COLUMNS:
+ return resetColumns(state);
+ case MSG_CLOSE_CONNECTION:
+ return closeConnection(state, action);
+ default:
+ return state;
+ }
+}
+
+module.exports = {
+ Messages,
+ messages,
+ getMessageDefaultColumnsState,
+};