Bug 1316834 - Adapt WebSocket Monitor extension to the new Net panel architecture draft
authorJarda Snajdr <jsnajdr@gmail.com>
Sat, 26 Nov 2016 10:22:43 +0100
changeset 444597 f0fb9d2533a51d3985a55efe52e559f5c293e9a2
parent 444596 39518e0b763ecfcda88cdfedbe0bb7714ec7418e
child 538328 1f16cc2df8b068c282a09ffe645de2fe0b46642d
push id37301
push userbmo:jsnajdr@gmail.com
push dateMon, 28 Nov 2016 09:33:10 +0000
bugs1316834
milestone53.0a1
Bug 1316834 - Adapt WebSocket Monitor extension to the new Net panel architecture MozReview-Commit-ID: IHAlgoSBXgB
devtools/client/netmonitor/components/request-list-content.js
devtools/client/netmonitor/components/request-list-item.js
devtools/client/netmonitor/events.js
devtools/client/netmonitor/reducers/requests.js
devtools/client/netmonitor/requests-menu-view.js
--- a/devtools/client/netmonitor/components/request-list-content.js
+++ b/devtools/client/netmonitor/components/request-list-content.js
@@ -194,16 +194,17 @@ const RequestListContent = createClass({
     }
   },
 
   render() {
     const { selectedRequestId,
             displayedRequests,
             firstRequestStartedMillis,
             onItemMouseDown,
+            onItemClick,
             onItemContextMenu,
             onSecurityIconClick } = this.props;
 
     return div(
       {
         ref: "contentEl",
         className: "requests-menu-contents",
         tabIndex: 0,
@@ -211,16 +212,17 @@ const RequestListContent = createClass({
       },
       displayedRequests.map((item, index) => RequestListItem({
         key: item.id,
         item,
         index,
         isSelected: item.id === selectedRequestId,
         firstRequestStartedMillis,
         onMouseDown: e => onItemMouseDown(e, item.id),
+        onClick: e => onItemClick(e, item),
         onContextMenu: e => onItemContextMenu(e, item.id),
         onSecurityIconClick: e => onSecurityIconClick(e, item),
         onFocusedNodeChange: this.onFocusedNodeChange,
         onFocusedNodeUnmount: this.onFocusedNodeUnmount,
       }))
     );
   },
 });
@@ -230,16 +232,19 @@ module.exports = connect(
     displayedRequests: getDisplayedRequests(state),
     selectedRequestId: state.requests.selectedId,
     scale: getWaterfallScale(state),
     firstRequestStartedMillis: state.requests.firstStartedMillis,
     tooltip: NetMonitorView.RequestsMenu.tooltip,
   }),
   dispatch => ({
     onItemMouseDown: (e, item) => dispatch(Actions.selectRequest(item)),
+    onItemClick: (e, item) => {
+      NetMonitorView.RequestsMenu.onItemClick(e, item);
+    },
     onItemContextMenu: (e, item) => {
       e.preventDefault();
       NetMonitorView.RequestsMenu.contextMenu.open(e);
     },
     onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
     /**
      * A handler that opens the security tab in the details view if secure or
      * broken security indicator is clicked.
--- a/devtools/client/netmonitor/components/request-list-item.js
+++ b/devtools/client/netmonitor/components/request-list-item.js
@@ -16,18 +16,19 @@ const { getAbbreviatedMimeType } = requi
 const RequestListItem = createClass({
   displayName: "RequestListItem",
 
   propTypes: {
     item: PropTypes.object.isRequired,
     index: PropTypes.number.isRequired,
     isSelected: PropTypes.bool.isRequired,
     firstRequestStartedMillis: PropTypes.number.isRequired,
-    onContextMenu: PropTypes.func.isRequired,
-    onMouseDown: PropTypes.func.isRequired,
+    onContextMenu: PropTypes.func,
+    onMouseDown: PropTypes.func,
+    onClick: PropTypes.func,
     onSecurityIconClick: PropTypes.func.isRequired,
   },
 
   componentDidMount() {
     if (this.props.isSelected) {
       this.refs.el.focus();
     }
   },
@@ -65,33 +66,38 @@ const RequestListItem = createClass({
   render() {
     const {
       item,
       index,
       isSelected,
       firstRequestStartedMillis,
       onContextMenu,
       onMouseDown,
+      onClick,
       onSecurityIconClick
     } = this.props;
 
     let classList = [ "request-list-item" ];
+    if (item.isWS) {
+      classList.push("websocket");
+    }
     if (isSelected) {
       classList.push("selected");
     }
     classList.push(index % 2 ? "odd" : "even");
 
     return div(
       {
         ref: "el",
         className: classList.join(" "),
         "data-id": item.id,
         tabIndex: 0,
         onContextMenu,
         onMouseDown,
+        onClick,
       },
       StatusColumn(item),
       MethodColumn(item),
       FileColumn(item),
       DomainColumn(item, onSecurityIconClick),
       CauseColumn(item),
       TypeColumn(item),
       TransferredSizeColumn(item),
@@ -109,16 +115,17 @@ const RequestListItem = createClass({
  */
 const RELEVANT_ITEM_PROPS = [
   "status",
   "statusText",
   "fromCache",
   "fromServiceWorker",
   "method",
   "url",
+  "isWS",
   "responseContentDataUri",
   "remoteAddress",
   "securityState",
   "cause",
   "mimeType",
   "contentSize",
   "transferredSize",
   "startedMillis",
--- a/devtools/client/netmonitor/events.js
+++ b/devtools/client/netmonitor/events.js
@@ -80,11 +80,14 @@ const EVENTS = {
   // Fired when charts have been displayed in the PerformanceStatisticsView.
   PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
   PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
   EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
 
   // Fired once the NetMonitorController establishes a connection to the debug
   // target.
   CONNECTED: "connected",
+
+  // Fired when user clicks on a request list row - used by extensions
+  REQUEST_ITEM_CLICKED: "NetMonitor::RequestItemClicked",
 };
 
 exports.EVENTS = EVENTS;
--- a/devtools/client/netmonitor/reducers/requests.js
+++ b/devtools/client/netmonitor/reducers/requests.js
@@ -24,16 +24,17 @@ const Request = I.Record({
   // Request properties - at the beginning, they are unknown and are gradually filled in
   startedMillis: undefined,
   method: undefined,
   url: undefined,
   urlDetails: undefined,
   remotePort: undefined,
   remoteAddress: undefined,
   isXHR: undefined,
+  isWS: undefined,
   cause: undefined,
   fromCache: undefined,
   fromServiceWorker: undefined,
   status: undefined,
   statusText: undefined,
   httpVersion: undefined,
   securityState: undefined,
   securityInfo: undefined,
@@ -134,16 +135,22 @@ function requestsReducer(state = new Req
 
           request[key] = value;
 
           switch (key) {
             case "url":
               // Compute the additional URL details
               request.urlDetails = getUrlDetails(value);
               break;
+            case "requestHeaders":
+              // Determine if this is a WebSocket handshake
+              request.isWS = Array.isArray(value.headers) && value.headers.some(
+                header => header.name === "Upgrade" && header.value === "websocket"
+              );
+              break;
             case "responseContent":
               // If there's no mime type available when the response content
               // is received, assume text/plain as a fallback.
               if (!request.mimeType) {
                 request.mimeType = "text/plain";
               }
               break;
             case "totalTime":
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -375,11 +375,15 @@ RequestsMenuView.prototype = {
   },
 
   /**
    * Remove the currently selected custom request.
    */
   closeCustomRequest() {
     this.store.dispatch(Actions.removeSelectedCustomRequest());
   },
+
+  onItemClick(e, item) {
+    window.emit(EVENTS.REQUEST_ITEM_CLICKED, e, item);
+  }
 };
 
 exports.RequestsMenuView = RequestsMenuView;