messages.js (4507B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 createSelector, 9 } = require("resource://devtools/client/shared/vendor/reselect.js"); 10 11 /** 12 * Returns list of messages that are visible to the user. 13 * Filtered messages by types and text are factored in. 14 */ 15 const getDisplayedMessages = createSelector( 16 state => state.messages, 17 ({ 18 messages, 19 messageFilterType, 20 showControlFrames, 21 messageFilterText, 22 currentChannelId, 23 }) => { 24 if (!currentChannelId || !messages.get(currentChannelId)) { 25 return []; 26 } 27 28 const messagesArray = messages.get(currentChannelId); 29 if (messageFilterType === "all" && messageFilterText.length === 0) { 30 return messagesArray.filter(message => 31 typeFilter(message, messageFilterType, showControlFrames) 32 ); 33 } 34 35 const filter = searchFilter(messageFilterText); 36 37 // If message payload is > 10,000 characters long, we check the LongStringActor payload string 38 return messagesArray.filter( 39 message => 40 (message.payload.initial 41 ? filter(message.payload.initial) 42 : filter(message.payload)) && 43 typeFilter(message, messageFilterType, showControlFrames) 44 ); 45 } 46 ); 47 48 function typeFilter(message, messageFilterType, showControlFrames) { 49 const controlFrames = [0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf]; 50 const isControlFrame = controlFrames.includes(message.opCode); 51 if (messageFilterType === "all" || messageFilterType === message.type) { 52 return showControlFrames || !isControlFrame; 53 } 54 return false; 55 } 56 57 function searchFilter(messageFilterText) { 58 let regex; 59 if (looksLikeRegex(messageFilterText)) { 60 try { 61 regex = regexFromText(messageFilterText); 62 } catch (e) {} 63 } 64 65 return regex 66 ? payload => regex.test(payload) 67 : payload => payload.includes(messageFilterText); 68 } 69 70 function looksLikeRegex(text) { 71 return text.startsWith("/") && text.endsWith("/") && text.length > 2; 72 } 73 74 function regexFromText(text) { 75 return new RegExp(text.slice(1, -1), "im"); 76 } 77 78 /** 79 * Checks if the selected message is visible. 80 * If the selected message is not visible, the SplitBox component 81 * should not show the MessagePayload component. 82 */ 83 const isSelectedMessageVisible = createSelector( 84 state => state.messages, 85 getDisplayedMessages, 86 ({ selectedMessage }, displayedMessages) => 87 displayedMessages.some(message => message === selectedMessage) 88 ); 89 90 /** 91 * Returns the current selected message. 92 */ 93 const getSelectedMessage = createSelector( 94 state => state.messages, 95 ({ selectedMessage }) => (selectedMessage ? selectedMessage : undefined) 96 ); 97 98 /** 99 * Returns summary data of the list of messages that are visible to the user. 100 * Filtered messages by types and text are factored in. 101 */ 102 const getDisplayedMessagesSummary = createSelector( 103 getDisplayedMessages, 104 displayedMessages => { 105 let firstStartedMs = +Infinity; 106 let lastEndedMs = -Infinity; 107 let sentSize = 0; 108 let receivedSize = 0; 109 let totalSize = 0; 110 111 displayedMessages.forEach(message => { 112 if (message.type == "received") { 113 receivedSize += message.payload.length; 114 } else if (message.type == "sent") { 115 sentSize += message.payload.length; 116 } 117 totalSize += message.payload.length; 118 if (message.timeStamp < firstStartedMs) { 119 firstStartedMs = message.timeStamp; 120 } 121 if (message.timeStamp > lastEndedMs) { 122 lastEndedMs = message.timeStamp; 123 } 124 }); 125 126 return { 127 count: displayedMessages.length, 128 totalMs: (lastEndedMs - firstStartedMs) / 1000, 129 sentSize, 130 receivedSize, 131 totalSize, 132 }; 133 } 134 ); 135 136 /** 137 * Returns if the currentChannelId is closed 138 */ 139 const isCurrentChannelClosed = createSelector( 140 state => state.messages, 141 ({ closedConnections, currentChannelId }) => 142 closedConnections.has(currentChannelId) 143 ); 144 145 /** 146 * Returns the closed connection details of the currentChannelId 147 * Null, if the connection is still open 148 */ 149 const getClosedConnectionDetails = createSelector( 150 state => state.messages, 151 ({ closedConnections, currentChannelId }) => 152 closedConnections.get(currentChannelId) 153 ); 154 155 module.exports = { 156 getSelectedMessage, 157 isSelectedMessageVisible, 158 getDisplayedMessages, 159 getDisplayedMessagesSummary, 160 isCurrentChannelClosed, 161 getClosedConnectionDetails, 162 };