Bug 1566780 - Added another filter frame type for viewing control frames separately r=Honza
authorKishlaya Jaiswal <kishlaya.j@gmail.com>
Tue, 31 Mar 2020 10:37:49 +0000
changeset 521247 5431843c523d2b17f8d263bbee0151f3c844e1ad
parent 521246 ebedd05e0d17283d20d2a4082c61cbc041765b54
child 521248 c4100c990ddccddae62224bc575090ee78a405be
push id37269
push useraiakab@mozilla.com
push dateTue, 31 Mar 2020 22:58:23 +0000
treeherdermozilla-central@9af589864188 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1566780
milestone76.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1566780 - Added another filter frame type for viewing control frames separately r=Honza Correcting linter errors Added new logic for toggling control frames by adding a new control frame check variable and toggle function Preserve controlFrames option when clearing frames. Changed Control Frames to Control in locale Correct linter errors Added a separator between received and control. Changed the dropdown title to reflect control frame state Changed controlFramesEnabled to showControlFrames Added a comment about showControlFrames and toggleControlFrames Change ws-early-connection test to check for only 2 frames, skipping the other 2 control frames Differential Revision: https://phabricator.services.mozilla.com/D67849
devtools/client/locales/en-US/netmonitor.properties
devtools/client/netmonitor/src/actions/web-sockets.js
devtools/client/netmonitor/src/components/websockets/FrameFilterMenu.js
devtools/client/netmonitor/src/components/websockets/Toolbar.js
devtools/client/netmonitor/src/constants.js
devtools/client/netmonitor/src/reducers/web-sockets.js
devtools/client/netmonitor/src/selectors/web-sockets.js
devtools/client/netmonitor/test/browser_net_ws-early-connection.js
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -700,16 +700,24 @@ netmonitor.ws.context.sent.accesskey=S
 # LOCALIZATION NOTE (netmonitor.ws.context.received): This is the label displayed
 # on the context menu that shows "Received" WebSocket frames.
 netmonitor.ws.context.received=Received
 
 # LOCALIZATION NOTE (netmonitor.ws.context.received.accesskey): This is the access key
 # for the "Received" menu item displayed in the context menu in the websocket toolbar.
 netmonitor.ws.context.received.accesskey=R
 
+# LOCALIZATION NOTE (netmonitor.ws.context.controlFrames): This is the label displayed
+# on the context menu that shows "Control Frames" WebSocket frames.
+netmonitor.ws.context.controlFrames=Control
+
+# LOCALIZATION NOTE (netmonitor.ws.context.controlFrames.accesskey): This is the access key
+# for the "Control Frames" menu item displayed in the context menu in the websocket toolbar.
+netmonitor.ws.context.controlFrames.accesskey=o
+
 # LOCALIZATION NOTE (netmonitor.ws.context.copyFrame): This is the label displayed
 # on the context menu that shows "Copy Message".
 netmonitor.ws.context.copyFrame=Copy Message
 
 # LOCALIZATION NOTE (netmonitor.ws.context.copyFrame.accesskey): This is the access key
 # for the "Copy Message" menu item displayed in the context menu of a WebSocket frame.
 netmonitor.ws.context.copyFrame.accesskey=C
 
--- a/devtools/client/netmonitor/src/actions/web-sockets.js
+++ b/devtools/client/netmonitor/src/actions/web-sockets.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const {
   WS_ADD_FRAME,
   WS_SELECT_FRAME,
   WS_OPEN_FRAME_DETAILS,
   WS_CLEAR_FRAMES,
   WS_TOGGLE_FRAME_FILTER_TYPE,
+  WS_TOGGLE_CONTROL_FRAMES,
   WS_SET_REQUEST_FILTER_TEXT,
   WS_TOGGLE_COLUMN,
   WS_RESET_COLUMNS,
   WS_CLOSE_CONNECTION,
 } = require("devtools/client/netmonitor/src/constants");
 
 const {
   getDisplayedFrames,
@@ -73,16 +74,26 @@ function clearFrames() {
 function toggleFrameFilterType(filter) {
   return {
     type: WS_TOGGLE_FRAME_FILTER_TYPE,
     filter,
   };
 }
 
 /**
+ * Show control frames from the FrameListContent
+ * component belonging to the current channelId
+ */
+function toggleControlFrames() {
+  return {
+    type: WS_TOGGLE_CONTROL_FRAMES,
+  };
+}
+
+/**
  * Set filter text in toolbar.
  *
  */
 function setFrameFilterText(text) {
   return {
     type: WS_SET_REQUEST_FILTER_TEXT,
     text,
   };
@@ -160,14 +171,15 @@ function selectFrameDelta(delta) {
 }
 
 module.exports = {
   addFrame,
   selectFrame,
   openFrameDetails,
   clearFrames,
   toggleFrameFilterType,
+  toggleControlFrames,
   setFrameFilterText,
   resetWebSocketsColumns,
   closeConnection,
   toggleWebSocketsColumn,
   selectFrameDelta,
 };
--- a/devtools/client/netmonitor/src/components/websockets/FrameFilterMenu.js
+++ b/devtools/client/netmonitor/src/components/websockets/FrameFilterMenu.js
@@ -17,63 +17,96 @@ loader.lazyRequireGetter(
   true
 );
 
 class FrameFilterMenu extends PureComponent {
   static get propTypes() {
     return {
       frameFilterType: PropTypes.string.isRequired,
       toggleFrameFilterType: PropTypes.func.isRequired,
+      // showControlFrames decides if control frames
+      // will be shown in messages panel
+      showControlFrames: PropTypes.bool.isRequired,
+      // toggleControlFrames toggles the value for showControlFrames
+      toggleControlFrames: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.onShowFilterMenu = this.onShowFilterMenu.bind(this);
   }
 
   onShowFilterMenu(event) {
-    const { frameFilterType, toggleFrameFilterType } = this.props;
+    const {
+      frameFilterType,
+      toggleFrameFilterType,
+      showControlFrames,
+      toggleControlFrames,
+    } = this.props;
 
     const menuItems = [
       {
         id: "ws-frame-list-context-filter-all",
         label: L10N.getStr("netmonitor.ws.context.all"),
         accesskey: L10N.getStr("netmonitor.ws.context.all.accesskey"),
+        type: "checkbox",
         checked: frameFilterType === "all",
         click: () => {
           toggleFrameFilterType("all");
         },
       },
       {
         id: "ws-frame-list-context-filter-sent",
         label: L10N.getStr("netmonitor.ws.context.sent"),
         accesskey: L10N.getStr("netmonitor.ws.context.sent.accesskey"),
+        type: "checkbox",
         checked: frameFilterType === "sent",
         click: () => {
           toggleFrameFilterType("sent");
         },
       },
       {
         id: "ws-frame-list-context-filter-received",
         label: L10N.getStr("netmonitor.ws.context.received"),
         accesskey: L10N.getStr("netmonitor.ws.context.received.accesskey"),
+        type: "checkbox",
         checked: frameFilterType === "received",
         click: () => {
           toggleFrameFilterType("received");
         },
       },
+      {
+        type: "separator",
+      },
+      {
+        id: "ws-frame-list-context-filter-controlFrames",
+        label: L10N.getStr("netmonitor.ws.context.controlFrames"),
+        accesskey: L10N.getStr("netmonitor.ws.context.controlFrames.accesskey"),
+        type: "checkbox",
+        checked: showControlFrames,
+        click: () => {
+          toggleControlFrames();
+        },
+      },
     ];
 
     showMenu(menuItems, { button: event.target });
   }
 
   render() {
-    const { frameFilterType } = this.props;
-    const title = L10N.getStr(`netmonitor.ws.context.${frameFilterType}`);
+    const { frameFilterType, showControlFrames } = this.props;
+    const frameFilterTypeTitle = L10N.getStr(
+      `netmonitor.ws.context.${frameFilterType}`
+    );
+    const title =
+      frameFilterTypeTitle +
+      (showControlFrames
+        ? " (" + L10N.getStr(`netmonitor.ws.context.controlFrames`) + ")"
+        : "");
 
     return dom.button(
       {
         id: "frame-filter-menu",
         className: "devtools-button devtools-dropdown-button",
         title,
         onClick: this.onShowFilterMenu,
       },
--- a/devtools/client/netmonitor/src/components/websockets/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/websockets/Toolbar.js
@@ -43,19 +43,21 @@ const WS_SEARCH_PLACE_HOLDER = L10N.getS
  * Toolbar contains a set of useful tools that clear the list of
  * existing frames as well as filter content.
  */
 class Toolbar extends Component {
   static get propTypes() {
     return {
       searchboxRef: PropTypes.object.isRequired,
       toggleFrameFilterType: PropTypes.func.isRequired,
+      toggleControlFrames: PropTypes.func.isRequired,
       clearFrames: PropTypes.func.isRequired,
       setFrameFilterText: PropTypes.func.isRequired,
       frameFilterType: PropTypes.string.isRequired,
+      showControlFrames: PropTypes.bool.isRequired,
     };
   }
 
   componentWillUnmount() {
     const { setFrameFilterText } = this.props;
     setFrameFilterText("");
   }
 
@@ -79,21 +81,28 @@ class Toolbar extends Component {
       },
     });
   }
 
   /**
    * Render the frame filter menu button.
    */
   renderFrameFilterMenu() {
-    const { frameFilterType, toggleFrameFilterType } = this.props;
+    const {
+      frameFilterType,
+      toggleFrameFilterType,
+      showControlFrames,
+      toggleControlFrames,
+    } = this.props;
 
     return FrameFilterMenu({
       frameFilterType,
       toggleFrameFilterType,
+      showControlFrames,
+      toggleControlFrames,
     });
   }
 
   /**
    * Render filter Searchbox.
    */
   renderFilterBox(setFrameFilterText) {
     return SearchBox({
@@ -121,16 +130,18 @@ class Toolbar extends Component {
       this.renderFilterBox(setFrameFilterText)
     );
   }
 }
 
 module.exports = connect(
   state => ({
     frameFilterType: state.webSockets.frameFilterType,
+    showControlFrames: state.webSockets.showControlFrames,
   }),
   dispatch => ({
     clearFrames: () => dispatch(Actions.clearFrames()),
     toggleFrameFilterType: filter =>
       dispatch(Actions.toggleFrameFilterType(filter)),
+    toggleControlFrames: () => dispatch(Actions.toggleControlFrames()),
     setFrameFilterText: text => dispatch(Actions.setFrameFilterText(text)),
   })
 )(Toolbar);
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -45,16 +45,17 @@ const actionTypes = {
   UPDATE_REQUEST: "UPDATE_REQUEST",
   WATERFALL_RESIZE: "WATERFALL_RESIZE",
   SET_COLUMNS_WIDTH: "SET_COLUMNS_WIDTH",
   WS_ADD_FRAME: "WS_ADD_FRAME",
   WS_SELECT_FRAME: "WS_SELECT_FRAME",
   WS_OPEN_FRAME_DETAILS: "WS_OPEN_FRAME_DETAILS",
   WS_CLEAR_FRAMES: "WS_CLEAR_FRAMES",
   WS_TOGGLE_FRAME_FILTER_TYPE: "WS_TOGGLE_FRAME_FILTER_TYPE",
+  WS_TOGGLE_CONTROL_FRAMES: "WS_TOGGLE_CONTROL_FRAMES",
   WS_SET_REQUEST_FILTER_TEXT: "WS_SET_REQUEST_FILTER_TEXT",
   WS_TOGGLE_COLUMN: "WS_TOGGLE_COLUMN",
   WS_RESET_COLUMNS: "WS_RESET_COLUMNS",
   WS_CLOSE_CONNECTION: "WS_CLOSE_CONNECTION",
 
   // Search
   ADD_SEARCH_QUERY: "ADD_SEARCH_QUERY",
   ADD_SEARCH_RESULT: "ADD_SEARCH_RESULT",
--- a/devtools/client/netmonitor/src/reducers/web-sockets.js
+++ b/devtools/client/netmonitor/src/reducers/web-sockets.js
@@ -6,16 +6,17 @@
 
 const {
   SELECT_REQUEST,
   WS_ADD_FRAME,
   WS_SELECT_FRAME,
   WS_OPEN_FRAME_DETAILS,
   WS_CLEAR_FRAMES,
   WS_TOGGLE_FRAME_FILTER_TYPE,
+  WS_TOGGLE_CONTROL_FRAMES,
   WS_SET_REQUEST_FILTER_TEXT,
   WS_TOGGLE_COLUMN,
   WS_RESET_COLUMNS,
   WS_CLOSE_CONNECTION,
 } = require("devtools/client/netmonitor/src/constants");
 
 /**
  * The default column state for the FramesListItem component.
@@ -42,16 +43,17 @@ function getWebSocketsDefaultColumnsStat
  */
 function WebSockets(initialState = {}) {
   return {
     // Map with all requests (key = channelId, value = array of frame objects)
     frames: new Map(),
     frameFilterText: "",
     // Default filter type is "all",
     frameFilterType: "all",
+    showControlFrames: false,
     selectedFrame: null,
     frameDetailsOpen: false,
     currentChannelId: null,
     closedConnections: new Map(),
     columns: getWebSocketsDefaultColumnsState(),
     ...initialState,
   };
 }
@@ -119,30 +121,41 @@ function clearFrames(state) {
     // Preserving the Map objects as they might contain state for other channelIds
     frames: nextState.frames,
     // Preserving the currentChannelId as there would not be another reset of channelId
     currentChannelId: nextState.currentChannelId,
     // Preserving the columns as they are set from pref
     columns: nextState.columns,
     frameFilterType: nextState.frameFilterType,
     frameFilterText: nextState.frameFilterText,
+    showControlFrames: nextState.showControlFrames,
   };
 }
 
 /**
  * Toggle the frame filter type of the WebSocket connection.
  */
 function toggleFrameFilterType(state, action) {
   return {
     ...state,
     frameFilterType: action.filter,
   };
 }
 
 /**
+ * Toggle control frame for the WebSocket connection.
+ */
+function toggleControlFrames(state, action) {
+  return {
+    ...state,
+    showControlFrames: !state.showControlFrames,
+  };
+}
+
+/**
  * Set the filter text of the current channelId.
  */
 function setFrameFilterText(state, action) {
   return {
     ...state,
     frameFilterText: action.text,
   };
 }
@@ -211,16 +224,18 @@ function webSockets(state = WebSockets()
     case WS_SELECT_FRAME:
       return selectFrame(state, action);
     case WS_OPEN_FRAME_DETAILS:
       return openFrameDetails(state, action);
     case WS_CLEAR_FRAMES:
       return clearFrames(state);
     case WS_TOGGLE_FRAME_FILTER_TYPE:
       return toggleFrameFilterType(state, action);
+    case WS_TOGGLE_CONTROL_FRAMES:
+      return toggleControlFrames(state, action);
     case WS_SET_REQUEST_FILTER_TEXT:
       return setFrameFilterText(state, action);
     case WS_TOGGLE_COLUMN:
       return toggleColumn(state, action);
     case WS_RESET_COLUMNS:
       return resetColumns(state);
     case WS_CLOSE_CONNECTION:
       return closeConnection(state, action);
--- a/devtools/client/netmonitor/src/selectors/web-sockets.js
+++ b/devtools/client/netmonitor/src/selectors/web-sockets.js
@@ -7,39 +7,56 @@
 const { createSelector } = require("devtools/client/shared/vendor/reselect");
 
 /**
  * Returns list of frames that are visible to the user.
  * Filtered frames by types and text are factored in.
  */
 const getDisplayedFrames = createSelector(
   state => state.webSockets,
-  ({ frames, frameFilterType, frameFilterText, currentChannelId }) => {
+  ({
+    frames,
+    frameFilterType,
+    showControlFrames,
+    frameFilterText,
+    currentChannelId,
+  }) => {
     if (!currentChannelId || !frames.get(currentChannelId)) {
       return [];
     }
 
     const framesArray = frames.get(currentChannelId);
     if (frameFilterType === "all" && frameFilterText.length === 0) {
-      return framesArray;
+      return framesArray.filter(frame =>
+        typeFilter(frame, frameFilterType, showControlFrames)
+      );
     }
 
     const filter = searchFilter(frameFilterText);
 
     // If frame payload is > 10,000 characters long, we check the LongStringActor payload string
     return framesArray.filter(
       frame =>
         (frame.payload.initial
           ? filter(frame.payload.initial)
           : filter(frame.payload)) &&
-        (frameFilterType === "all" || frameFilterType === frame.type)
+        typeFilter(frame, frameFilterType, showControlFrames)
     );
   }
 );
 
+function typeFilter(frame, frameFilterType, showControlFrames) {
+  const controlFrames = [0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf];
+  const isControlFrame = controlFrames.includes(frame.opCode);
+  if (frameFilterType === "all" || frameFilterType === frame.type) {
+    return showControlFrames || !isControlFrame;
+  }
+  return false;
+}
+
 function searchFilter(frameFilterText) {
   let regex;
   if (looksLikeRegex(frameFilterText)) {
     try {
       regex = regexFromText(frameFilterText);
     } catch (e) {}
   }
 
--- a/devtools/client/netmonitor/test/browser_net_ws-early-connection.js
+++ b/devtools/client/netmonitor/test/browser_net_ws-early-connection.js
@@ -48,17 +48,17 @@ add_task(async function() {
   EventUtils.sendMouseEvent({ type: "mousedown" }, requests[index]);
 
   info("Waiting for WS frames...");
 
   // Wait for two frames to be displayed in the panel
   await waitForDOMIfNeeded(
     document,
     "#messages-panel .ws-frames-list-table .ws-frame-list-item",
-    4
+    2
   );
 
   // Check the payload of the first frame.
   const firstFramePayload = document.querySelector(
     "#messages-panel .ws-frames-list-table .ws-frame-list-item .ws-frames-list-payload"
   );
   is(firstFramePayload.textContent.trim(), "readyState:loading");