Bug 1555625 - Basic skeleton of side panel for WS frames. r=Honza

Implement basic skeleton of side panel for WS frames.

Differential Revision: https://phabricator.services.mozilla.com/D34704

--HG--
extra : moz-landing-system : lando
This commit is contained in:
tanhengyeow 2019-06-19 20:00:30 +00:00
Родитель 9e137b1e80
Коммит 1f04945904
20 изменённых файлов: 250 добавлений и 21 удалений

Просмотреть файл

@ -66,6 +66,10 @@ headersEmptyText=No headers for this request
# headers tab of the network details pane for the filtering input.
headersFilterText=Filter headers
# LOCALIZATION NOTE (webSocketsEmptyText): This is the text displayed in the
# WebSockets tab of the network details pane when there are no frames available.
webSocketsEmptyText=No WebSocket frames for this request
# LOCALIZATION NOTE (cookiesEmptyText): This is the text displayed in the
# cookies tab of the network details pane when there are no cookies available.
cookiesEmptyText=No cookies for this request
@ -637,6 +641,10 @@ netmonitor.toolbar.waterfall=Timeline
# in the network details pane identifying the headers tab.
netmonitor.tab.headers=Headers
# LOCALIZATION NOTE (netmonitor.tab.webSockets): This is the label displayed
# in the network details pane identifying the webSockets tab.
netmonitor.tab.webSockets=WebSockets
# LOCALIZATION NOTE (netmonitor.tab.cookies): This is the label displayed
# in the network details pane identifying the cookies tab.
netmonitor.tab.cookies=Cookies

Просмотреть файл

@ -11,6 +11,7 @@ const selection = require("./selection");
const sort = require("./sort");
const timingMarkers = require("./timing-markers");
const ui = require("./ui");
const webSockets = require("./web-sockets");
Object.assign(exports,
batching,
@ -19,5 +20,6 @@ Object.assign(exports,
selection,
sort,
timingMarkers,
ui
ui,
webSockets,
);

Просмотреть файл

@ -11,4 +11,5 @@ DevToolsModules(
'sort.js',
'timing-markers.js',
'ui.js',
'web-sockets.js',
)

Просмотреть файл

@ -0,0 +1,19 @@
/* 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 { WS_ADD_FRAME } = require("../constants");
function addFrame(httpChannelId, data) {
return {
type: WS_ADD_FRAME,
httpChannelId,
data,
};
}
module.exports = {
addFrame,
};

Просмотреть файл

@ -4,6 +4,7 @@
"use strict";
const Services = require("Services");
const {
Component,
createFactory,
@ -17,6 +18,7 @@ const Tabbar = createFactory(require("devtools/client/shared/components/tabs/Tab
const TabPanel = createFactory(require("devtools/client/shared/components/tabs/Tabs").TabPanel);
const CookiesPanel = createFactory(require("./CookiesPanel"));
const HeadersPanel = createFactory(require("./HeadersPanel"));
const WebSocketsPanel = createFactory(require("./WebSocketsPanel"));
const ParamsPanel = createFactory(require("./ParamsPanel"));
const CachePanel = createFactory(require("./CachePanel"));
const ResponsePanel = createFactory(require("./ResponsePanel"));
@ -28,6 +30,7 @@ const COLLAPSE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
const CACHE_TITLE = L10N.getStr("netmonitor.tab.cache");
const COOKIES_TITLE = L10N.getStr("netmonitor.tab.cookies");
const HEADERS_TITLE = L10N.getStr("netmonitor.tab.headers");
const WEBSOCKETS_TITLE = L10N.getStr("netmonitor.tab.webSockets");
const PARAMS_TITLE = L10N.getStr("netmonitor.tab.params");
const RESPONSE_TITLE = L10N.getStr("netmonitor.tab.response");
const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
@ -87,6 +90,11 @@ class TabboxPanel extends Component {
return null;
}
const channelId = request.channelId;
const showWebSocketsPanel =
request.cause.type === "websocket" &&
Services.prefs.getBoolPref(
"devtools.netmonitor.features.webSockets");
return (
Tabbar({
activeTabId,
@ -113,6 +121,14 @@ class TabboxPanel extends Component {
request,
}),
),
showWebSocketsPanel && TabPanel({
id: PANELS.WEBSOCKETS,
title: WEBSOCKETS_TITLE,
},
WebSocketsPanel({
channelId,
}),
),
TabPanel({
id: PANELS.COOKIES,
title: COOKIES_TITLE,

Просмотреть файл

@ -0,0 +1,85 @@
/* 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 { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
connect,
} = require("devtools/client/shared/redux/visibility-handler-connect");
const { getFramesByChannelId } = require("../selectors/index");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { table, tbody, thead, tr, td, th, div } = dom;
const { L10N } = require("../utils/l10n");
const FRAMES_EMPTY_TEXT = L10N.getStr("webSocketsEmptyText");
class WebSocketsPanel extends Component {
static get propTypes() {
return {
channelId: PropTypes.number,
frames: PropTypes.array,
};
}
constructor(props) {
super(props);
}
render() {
const { frames } = this.props;
if (!frames) {
return div({ className: "empty-notice" },
FRAMES_EMPTY_TEXT
);
}
const rows = [];
frames.forEach((frame, index) => {
rows.push(
tr(
{ key: index,
className: "frames-row" },
td({ className: "frames-cell" }, frame.type),
td({ className: "frames-cell" }, frame.httpChannelId),
td({ className: "frames-cell" }, frame.payload),
td({ className: "frames-cell" }, frame.opCode),
td({ className: "frames-cell" }, frame.maskBit.toString()),
td({ className: "frames-cell" }, frame.finBit.toString()),
td({ className: "frames-cell" }, frame.timeStamp)
)
);
});
return table(
{ className: "frames-list-table" },
thead(
{ className: "frames-head" },
tr(
{ className: "frames-row" },
th({ className: "frames-headerCell" }, "Type"),
th({ className: "frames-headerCell" }, "Channel ID"),
th({ className: "frames-headerCell" }, "Payload"),
th({ className: "frames-headerCell" }, "OpCode"),
th({ className: "frames-headerCell" }, "MaskBit"),
th({ className: "frames-headerCell" }, "FinBit"),
th({ className: "frames-headerCell" }, "Time")
)
),
tbody(
{
className: "frames-list-tableBody",
},
rows
)
);
}
}
module.exports = connect((state, props) => ({
frames: getFramesByChannelId(state, props.channelId),
}))(WebSocketsPanel);

Просмотреть файл

@ -47,4 +47,5 @@ DevToolsModules(
'TabboxPanel.js',
'TimingsPanel.js',
'Toolbar.js',
'WebSocketsPanel.js',
)

Просмотреть файл

@ -79,6 +79,7 @@ class FirefoxDataProvider {
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
channelId,
} = data;
// Insert blocked reason in the payload queue as well, as we'll need it later
@ -106,6 +107,7 @@ class FirefoxDataProvider {
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
channelId,
}, true);
}
@ -343,6 +345,7 @@ class FirefoxDataProvider {
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
channelId,
} = networkInfo;
await this.addRequest(actor, {
@ -356,6 +359,7 @@ class FirefoxDataProvider {
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
channelId,
});
this.emit(EVENTS.NETWORK_EVENT, actor);
@ -426,11 +430,6 @@ class FirefoxDataProvider {
* @param {string} extensions
*/
async onWebSocketOpened(httpChannelId, effectiveURI, protocols, extensions) {
console.log("FirefoxDataProvider onWebSocketOpened: " +
" httpChannelId: " + httpChannelId +
" effectiveURI: " + effectiveURI +
" protocols: " + protocols +
" extensions: " + extensions);
}
/**
@ -441,10 +440,6 @@ class FirefoxDataProvider {
* @param {string} reason
*/
async onWebSocketClosed(wasClean, code, reason) {
console.log("FirefoxDataProvider onWebSocketClosed: " +
" wasClean: " + wasClean +
" code: " + code +
" reason: " + reason);
}
/**
@ -454,11 +449,7 @@ class FirefoxDataProvider {
* @param {object} data websocket frame information
*/
async onFrameSent(httpChannelId, data) {
await this.getLongString(data.payload).then(payload => {
console.log("FirefoxDataProvider onFrameSent: " +
" httpChannelId: " + httpChannelId +
" onFrameSent: " + payload);
});
this.addFrame(httpChannelId, data);
}
/**
@ -468,11 +459,20 @@ class FirefoxDataProvider {
* @param {object} data websocket frame information
*/
async onFrameReceived(httpChannelId, data) {
await this.getLongString(data.payload).then(payload => {
console.log("FirefoxDataProvider onFrameReceived: " +
" httpChannelId: " + httpChannelId +
" onFrameSent: " + payload);
});
this.addFrame(httpChannelId, data);
}
/**
* Add a new WebSocket frame to application state.
*
* @param {number} httpChannelId the channel ID
* @param {object} data websocket frame information
*/
async addFrame(httpChannelId, data) {
if (this.actionsEnabled && this.actions.addFrame) {
await this.actions.addFrame(httpChannelId, data);
}
// TODO: Emit an event for test here
}
/**

Просмотреть файл

@ -34,6 +34,7 @@ const actionTypes = {
UPDATE_REQUEST: "UPDATE_REQUEST",
WATERFALL_RESIZE: "WATERFALL_RESIZE",
SET_COLUMNS_WIDTH: "SET_COLUMNS_WIDTH",
WS_ADD_FRAME: "WS_ADD_FRAME",
};
// Descriptions for what this frontend is currently doing.
@ -154,11 +155,13 @@ const UPDATE_PROPS = [
"isThirdPartyTrackingResource",
"referrerPolicy",
"blockedReason",
"channelId",
];
const PANELS = {
COOKIES: "cookies",
HEADERS: "headers",
WEBSOCKETS: "webSockets",
PARAMS: "params",
RESPONSE: "response",
CACHE: "cache",

Просмотреть файл

@ -27,6 +27,7 @@ const { Requests } = require("./reducers/requests");
const { Sort } = require("./reducers/sort");
const { TimingMarkers } = require("./reducers/timing-markers");
const { UI, Columns, ColumnsData } = require("./reducers/ui");
const { WebSockets } = require("./reducers/web-sockets");
/**
* Configure state and middleware for the Network monitor tool.
@ -44,6 +45,7 @@ function configureStore(connector, telemetry) {
columns: getColumnState(),
columnsData: getColumnsData(),
}),
webSockets: new WebSockets(),
};
// Prepare middleware.

Просмотреть файл

@ -11,12 +11,14 @@ const { sortReducer } = require("./sort");
const { filters } = require("./filters");
const { timingMarkers } = require("./timing-markers");
const { ui } = require("./ui");
const { webSocketsReducer } = require("./web-sockets");
const networkThrottling = require("devtools/client/shared/components/throttling/reducer");
module.exports = batchingReducer(
combineReducers({
requests: requestsReducer,
sort: sortReducer,
webSockets: webSocketsReducer,
filters,
timingMarkers,
ui,

Просмотреть файл

@ -10,4 +10,5 @@ DevToolsModules(
'sort.js',
'timing-markers.js',
'ui.js',
'web-sockets.js',
)

Просмотреть файл

@ -0,0 +1,64 @@
/* 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 {
WS_ADD_FRAME,
} = require("../constants");
/**
* This structure stores list of all WebSocket frames received
* from the backend.
*/
function WebSockets() {
return {
// Map with all requests (key = channelId, value = array of frame objects)
frames: new Map(),
};
}
/**
* This reducer is responsible for maintaining list of
* WebSocket frames within the Network panel.
*/
function webSocketsReducer(state = WebSockets(), action) {
switch (action.type) {
// Appending new frame into the map.
case WS_ADD_FRAME: {
const nextState = { ...state };
const newFrame = {
httpChannelId: action.httpChannelId,
...action.data,
};
nextState.frames = mapSet(state.frames, newFrame.httpChannelId, newFrame);
return nextState;
}
default:
return state;
}
}
/**
* 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 framesArray = [...newMap.get(key)];
framesArray.push(value);
newMap.set(key, framesArray);
return newMap;
}
return newMap.set(key, [value]);
}
module.exports = {
WebSockets,
webSocketsReducer,
};

Просмотреть файл

@ -7,9 +7,11 @@
const requests = require("./requests");
const timingMarkers = require("./timing-markers");
const ui = require("./ui");
const webSockets = require("./web-sockets");
Object.assign(exports,
requests,
timingMarkers,
ui
ui,
webSockets,
);

Просмотреть файл

@ -7,4 +7,5 @@ DevToolsModules(
'requests.js',
'timing-markers.js',
'ui.js',
'web-sockets.js',
)

Просмотреть файл

@ -0,0 +1,13 @@
/* 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";
function getFramesByChannelId(state, channelId) {
return state.webSockets.frames.get(channelId);
}
module.exports = {
getFramesByChannelId,
};

Просмотреть файл

@ -75,6 +75,10 @@ function getCleanedPacket(key, packet) {
res.actor = existingPacket.actor;
}
if (res.channelId) {
res.channelId = existingPacket.channelId;
}
if (res.message) {
// Clean timeStamp on the message prop.
res.message.timeStamp = existingPacket.message.timeStamp;

Просмотреть файл

@ -265,6 +265,7 @@ stubPackets.set(`GET request`, {
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"channelId": 22673132355586,
"from": "server1.conn0.child1/consoleActor2"
});
@ -315,6 +316,7 @@ stubPackets.set(`XHR GET request`, {
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"channelId": 22673132355587,
"from": "server1.conn1.child1/consoleActor2"
});
@ -365,6 +367,7 @@ stubPackets.set(`XHR POST request`, {
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"channelId": 22673132355588,
"from": "server1.conn2.child1/consoleActor2"
});

Просмотреть файл

@ -68,6 +68,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
isThirdPartyTrackingResource: this._isThirdPartyTrackingResource,
referrerPolicy: this._referrerPolicy,
blockedReason: this._blockedReason,
channelId: this._channelId,
};
},

Просмотреть файл

@ -97,6 +97,7 @@ class WebConsoleFront extends FrontClassWithSpec(webconsoleSpec) {
isThirdPartyTrackingResource: actor.isThirdPartyTrackingResource,
referrerPolicy: actor.referrerPolicy,
blockedReason: actor.blockedReason,
channelId: actor.channelId,
};
this._networkRequests.set(actor.actor, networkInfo);