mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 21:05:36 +00:00
Bug 1408182 - Replace ImmutableJS by plain JS code; r=rickychien
MozReview-Commit-ID: 4f9Bv3XDuoc --HG-- extra : rebase_source : 834d1cfae3e32054fcaad6faa3569c4c3a885287
This commit is contained in:
parent
7afd6261d5
commit
38ebed24c2
@ -20,4 +20,10 @@ module.exports = {
|
||||
/* eslint-disable max-len */
|
||||
"mozilla/reject-some-requires": ["error", "^(chrome|chrome:.*|resource:.*|devtools/server/.*|.*\\.jsm|devtools/shared/platform/(chome|content)/.*)$"],
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
experimentalObjectRestSpread: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ class RequestListContent extends Component {
|
||||
connector: PropTypes.object.isRequired,
|
||||
columns: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
displayedRequests: PropTypes.object.isRequired,
|
||||
displayedRequests: PropTypes.array.isRequired,
|
||||
firstRequestStartedMillis: PropTypes.number.isRequired,
|
||||
fromCache: PropTypes.bool,
|
||||
onCauseBadgeMouseDown: PropTypes.func.isRequired,
|
||||
|
@ -40,7 +40,7 @@ class StatisticsPanel extends Component {
|
||||
connector: PropTypes.object.isRequired,
|
||||
closeStatistics: PropTypes.func.isRequired,
|
||||
enableRequestFilterTypeOnly: PropTypes.func.isRequired,
|
||||
requests: PropTypes.object,
|
||||
requests: PropTypes.array,
|
||||
};
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ class StatisticsPanel extends Component {
|
||||
MediaQueryList.addListener(this.onLayoutChange);
|
||||
|
||||
const { requests } = this.props;
|
||||
let ready = requests && !requests.isEmpty() && requests.every((req) =>
|
||||
let ready = requests && requests.length && requests.every((req) =>
|
||||
req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
|
||||
req.status !== undefined && req.totalTime !== undefined
|
||||
);
|
||||
|
@ -71,7 +71,7 @@ class Toolbar extends Component {
|
||||
toggleBrowserCache: PropTypes.func.isRequired,
|
||||
browserCacheDisabled: PropTypes.bool.isRequired,
|
||||
toggleRequestFilterType: PropTypes.func.isRequired,
|
||||
filteredRequests: PropTypes.object.isRequired,
|
||||
filteredRequests: PropTypes.array.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const I = require("devtools/client/shared/vendor/immutable");
|
||||
const {
|
||||
getUrlDetails,
|
||||
processNetworkUpdates,
|
||||
@ -21,59 +20,173 @@ const {
|
||||
UPDATE_REQUEST,
|
||||
} = require("../constants");
|
||||
|
||||
const Request = I.Record({
|
||||
id: null,
|
||||
// Set to true in case of a request that's being edited as part of "edit and resend"
|
||||
isCustom: false,
|
||||
// Request properties - at the beginning, they are unknown and are gradually filled in
|
||||
startedMillis: undefined,
|
||||
endedMillis: undefined,
|
||||
method: undefined,
|
||||
url: undefined,
|
||||
urlDetails: undefined,
|
||||
remotePort: undefined,
|
||||
remoteAddress: undefined,
|
||||
isXHR: undefined,
|
||||
cause: undefined,
|
||||
fromCache: undefined,
|
||||
fromServiceWorker: undefined,
|
||||
status: undefined,
|
||||
statusText: undefined,
|
||||
httpVersion: undefined,
|
||||
securityState: undefined,
|
||||
securityInfo: undefined,
|
||||
mimeType: "text/plain",
|
||||
contentSize: undefined,
|
||||
transferredSize: undefined,
|
||||
totalTime: undefined,
|
||||
eventTimings: undefined,
|
||||
headersSize: undefined,
|
||||
// Text value is used for storing custom request query
|
||||
// which only appears when user edit the custom requst form
|
||||
customQueryValue: undefined,
|
||||
requestHeaders: undefined,
|
||||
requestHeadersFromUploadStream: undefined,
|
||||
requestCookies: undefined,
|
||||
requestPostData: undefined,
|
||||
responseHeaders: undefined,
|
||||
responseCookies: undefined,
|
||||
responseContent: undefined,
|
||||
responseContentAvailable: false,
|
||||
formDataSections: undefined,
|
||||
});
|
||||
/**
|
||||
* This structure stores list of all HTTP requests received
|
||||
* from the backend. It's using plain JS structures to store
|
||||
* data instead of ImmutableJS, which is performance expensive.
|
||||
*/
|
||||
function Requests() {
|
||||
return {
|
||||
// Map with all requests (key = actor ID, value = request object)
|
||||
requests: mapNew(),
|
||||
// Selected request ID
|
||||
selectedId: null,
|
||||
preselectedId: null,
|
||||
// True if the monitor is recording HTTP traffic
|
||||
recording: true,
|
||||
// Auxiliary fields to hold requests stats
|
||||
firstStartedMillis: +Infinity,
|
||||
lastEndedMillis: -Infinity,
|
||||
};
|
||||
}
|
||||
|
||||
const Requests = I.Record({
|
||||
// The collection of requests (keyed by id)
|
||||
requests: I.Map(),
|
||||
// Selection state
|
||||
selectedId: null,
|
||||
preselectedId: null,
|
||||
// Auxiliary fields to hold requests stats
|
||||
firstStartedMillis: +Infinity,
|
||||
lastEndedMillis: -Infinity,
|
||||
// Recording state
|
||||
recording: true,
|
||||
});
|
||||
/**
|
||||
* This reducer is responsible for maintaining list of request
|
||||
* within the Network panel.
|
||||
*/
|
||||
function requestsReducer(state = Requests(), action) {
|
||||
switch (action.type) {
|
||||
// Appending new request into the list/map.
|
||||
case ADD_REQUEST: {
|
||||
let nextState = { ...state };
|
||||
|
||||
let newRequest = {
|
||||
id: action.id,
|
||||
...action.data,
|
||||
urlDetails: getUrlDetails(action.data.url),
|
||||
};
|
||||
|
||||
nextState.requests = mapSet(state.requests, newRequest.id, newRequest);
|
||||
|
||||
// Update the started/ended timestamps.
|
||||
let { startedMillis } = action.data;
|
||||
if (startedMillis < state.firstStartedMillis) {
|
||||
nextState.firstStartedMillis = startedMillis;
|
||||
}
|
||||
if (startedMillis > state.lastEndedMillis) {
|
||||
nextState.lastEndedMillis = startedMillis;
|
||||
}
|
||||
|
||||
// Select the request if it was preselected and there is no other selection.
|
||||
if (state.preselectedId && state.preselectedId === action.id) {
|
||||
nextState.selectedId = state.selectedId || state.preselectedId;
|
||||
nextState.preselectedId = null;
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
// Update an existing request (with received data).
|
||||
case UPDATE_REQUEST: {
|
||||
let { requests, lastEndedMillis } = state;
|
||||
|
||||
let request = requests.get(action.id);
|
||||
if (!request) {
|
||||
return state;
|
||||
}
|
||||
|
||||
request = {
|
||||
...request,
|
||||
...processNetworkUpdates(action.data),
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
requests: mapSet(state.requests, action.id, request),
|
||||
lastEndedMillis: lastEndedMillis,
|
||||
};
|
||||
}
|
||||
|
||||
// Remove all requests in the list. Create fresh new state
|
||||
// object, but keep value of the `recording` field.
|
||||
case CLEAR_REQUESTS: {
|
||||
return {
|
||||
...Requests(),
|
||||
recording: state.recording,
|
||||
};
|
||||
}
|
||||
|
||||
// Select specific request.
|
||||
case SELECT_REQUEST: {
|
||||
return {
|
||||
...state,
|
||||
selectedId: action.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Clone selected request for re-send.
|
||||
case CLONE_SELECTED_REQUEST: {
|
||||
let { requests, selectedId } = state;
|
||||
|
||||
if (!selectedId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let clonedRequest = requests.get(selectedId);
|
||||
if (!clonedRequest) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let newRequest = {
|
||||
id: clonedRequest.id + "-clone",
|
||||
method: clonedRequest.method,
|
||||
url: clonedRequest.url,
|
||||
urlDetails: clonedRequest.urlDetails,
|
||||
requestHeaders: clonedRequest.requestHeaders,
|
||||
requestPostData: clonedRequest.requestPostData,
|
||||
isCustom: true
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
requests: mapSet(requests, newRequest.id, newRequest),
|
||||
selectedId: newRequest.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Removing temporary cloned request (created for re-send, but canceled).
|
||||
case REMOVE_SELECTED_CUSTOM_REQUEST: {
|
||||
return closeCustomRequest(state);
|
||||
}
|
||||
|
||||
// Re-sending an existing request.
|
||||
case SEND_CUSTOM_REQUEST: {
|
||||
// When a new request with a given id is added in future, select it immediately.
|
||||
// where we know in advance the ID of the request, at a time when it
|
||||
// wasn't sent yet.
|
||||
return closeCustomRequest(state.set("preselectedId", action.id));
|
||||
}
|
||||
|
||||
// Pause/resume button clicked.
|
||||
case TOGGLE_RECORDING: {
|
||||
return {
|
||||
...state,
|
||||
recording: !state.recording,
|
||||
};
|
||||
}
|
||||
|
||||
// Side bar with request details opened.
|
||||
case OPEN_NETWORK_DETAILS: {
|
||||
let nextState = { ...state };
|
||||
if (!action.open) {
|
||||
nextState.selectedId = null;
|
||||
return nextState;
|
||||
}
|
||||
|
||||
if (!state.selectedId && !state.requests.isEmpty()) {
|
||||
nextState.selectedId = [...state.requests.values()][0].id;
|
||||
return nextState;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
/**
|
||||
* Remove the currently selected custom request.
|
||||
@ -92,119 +205,41 @@ function closeCustomRequest(state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return state.withMutations(st => {
|
||||
st.requests = st.requests.delete(selectedId);
|
||||
st.selectedId = null;
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
requests: mapDelete(state.requests, selectedId),
|
||||
selectedId: null,
|
||||
};
|
||||
}
|
||||
|
||||
function requestsReducer(state = new Requests(), action) {
|
||||
switch (action.type) {
|
||||
case ADD_REQUEST: {
|
||||
return state.withMutations(st => {
|
||||
let newRequest = new Request(Object.assign(
|
||||
{ id: action.id },
|
||||
action.data,
|
||||
{ urlDetails: getUrlDetails(action.data.url) }
|
||||
));
|
||||
st.requests = st.requests.set(newRequest.id, newRequest);
|
||||
// Immutability helpers
|
||||
// FIXME The following helper API need refactoring, see bug 1418969.
|
||||
|
||||
// Update the started/ended timestamps
|
||||
let { startedMillis } = action.data;
|
||||
if (startedMillis < st.firstStartedMillis) {
|
||||
st.firstStartedMillis = startedMillis;
|
||||
}
|
||||
if (startedMillis > st.lastEndedMillis) {
|
||||
st.lastEndedMillis = startedMillis;
|
||||
}
|
||||
/**
|
||||
* Clone an existing map.
|
||||
*/
|
||||
function mapNew(map) {
|
||||
let newMap = new Map(map);
|
||||
newMap.isEmpty = () => newMap.size == 0;
|
||||
newMap.valueSeq = () => [...newMap.values()];
|
||||
return newMap;
|
||||
}
|
||||
|
||||
// Select the request if it was preselected and there is no other selection
|
||||
if (st.preselectedId && st.preselectedId === action.id) {
|
||||
st.selectedId = st.selectedId || st.preselectedId;
|
||||
st.preselectedId = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
case CLEAR_REQUESTS: {
|
||||
return new Requests({
|
||||
recording: state.recording
|
||||
});
|
||||
}
|
||||
case CLONE_SELECTED_REQUEST: {
|
||||
let { requests, selectedId } = state;
|
||||
/**
|
||||
* Append new item into existing map and return new map.
|
||||
*/
|
||||
function mapSet(map, key, value) {
|
||||
let newMap = mapNew(map);
|
||||
return newMap.set(key, value);
|
||||
}
|
||||
|
||||
if (!selectedId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let clonedRequest = requests.get(selectedId);
|
||||
if (!clonedRequest) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let newRequest = new Request({
|
||||
id: clonedRequest.id + "-clone",
|
||||
method: clonedRequest.method,
|
||||
url: clonedRequest.url,
|
||||
urlDetails: clonedRequest.urlDetails,
|
||||
requestHeaders: clonedRequest.requestHeaders,
|
||||
requestPostData: clonedRequest.requestPostData,
|
||||
isCustom: true
|
||||
});
|
||||
|
||||
return state.withMutations(st => {
|
||||
st.requests = requests.set(newRequest.id, newRequest);
|
||||
st.selectedId = newRequest.id;
|
||||
});
|
||||
}
|
||||
case OPEN_NETWORK_DETAILS: {
|
||||
if (!action.open) {
|
||||
return state.set("selectedId", null);
|
||||
}
|
||||
|
||||
if (!state.selectedId && !state.requests.isEmpty()) {
|
||||
return state.set("selectedId", state.requests.first().id);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
case REMOVE_SELECTED_CUSTOM_REQUEST: {
|
||||
return closeCustomRequest(state);
|
||||
}
|
||||
case SELECT_REQUEST: {
|
||||
return state.set("selectedId", action.id);
|
||||
}
|
||||
case SEND_CUSTOM_REQUEST: {
|
||||
// When a new request with a given id is added in future, select it immediately.
|
||||
// where we know in advance the ID of the request, at a time when it
|
||||
// wasn't sent yet.
|
||||
return closeCustomRequest(state.set("preselectedId", action.id));
|
||||
}
|
||||
case TOGGLE_RECORDING: {
|
||||
return state.set("recording", !state.recording);
|
||||
}
|
||||
case UPDATE_REQUEST: {
|
||||
let { requests, lastEndedMillis } = state;
|
||||
|
||||
let updatedRequest = requests.get(action.id);
|
||||
if (!updatedRequest) {
|
||||
return state;
|
||||
}
|
||||
|
||||
updatedRequest = updatedRequest.withMutations(request => {
|
||||
let values = processNetworkUpdates(action.data);
|
||||
request = Object.assign(request, values);
|
||||
});
|
||||
|
||||
return state.withMutations(st => {
|
||||
st.requests = requests.set(updatedRequest.id, updatedRequest);
|
||||
st.lastEndedMillis = lastEndedMillis;
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
/**
|
||||
* Remove an item from existing map and return new map.
|
||||
*/
|
||||
function mapDelete(map, key) {
|
||||
let newMap = mapNew(map);
|
||||
newMap.requests.delete(key);
|
||||
return newMap;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -56,9 +56,9 @@ const getTypeFilterFn = createSelector(
|
||||
);
|
||||
|
||||
const getSortFn = createSelector(
|
||||
state => state.requests.requests,
|
||||
state => state.requests,
|
||||
state => state.sort,
|
||||
(requests, sort) => {
|
||||
({ requests }, sort) => {
|
||||
const sorter = Sorters[sort.type || "waterfall"];
|
||||
const ascending = sort.ascending ? +1 : -1;
|
||||
return (a, b) => ascending * sortWithClones(requests, sorter, a, b);
|
||||
@ -66,23 +66,34 @@ const getSortFn = createSelector(
|
||||
);
|
||||
|
||||
const getSortedRequests = createSelector(
|
||||
state => state.requests.requests,
|
||||
state => state.requests,
|
||||
getSortFn,
|
||||
(requests, sortFn) => requests.valueSeq().sort(sortFn).toList()
|
||||
({ requests }, sortFn) => {
|
||||
let arr = requests.valueSeq().sort(sortFn);
|
||||
arr.get = index => arr[index];
|
||||
arr.isEmpty = () => this.length == 0;
|
||||
arr.size = arr.length;
|
||||
return arr;
|
||||
}
|
||||
);
|
||||
|
||||
const getDisplayedRequests = createSelector(
|
||||
state => state.requests.requests,
|
||||
state => state.requests,
|
||||
getFilterFn,
|
||||
getSortFn,
|
||||
(requests, filterFn, sortFn) => requests.valueSeq()
|
||||
.filter(filterFn).sort(sortFn).toList()
|
||||
({ requests }, filterFn, sortFn) => {
|
||||
let arr = requests.valueSeq().filter(filterFn).sort(sortFn);
|
||||
arr.get = index => arr[index];
|
||||
arr.isEmpty = () => this.length == 0;
|
||||
arr.size = arr.length;
|
||||
return arr;
|
||||
}
|
||||
);
|
||||
|
||||
const getTypeFilteredRequests = createSelector(
|
||||
state => state.requests.requests,
|
||||
state => state.requests,
|
||||
getTypeFilterFn,
|
||||
(requests, filterFn) => requests.valueSeq().filter(filterFn).toList()
|
||||
({ requests }, filterFn) => requests.valueSeq().filter(filterFn)
|
||||
);
|
||||
|
||||
const getDisplayedRequestsSummary = createSelector(
|
||||
|
@ -5,7 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
function getDisplayedTimingMarker(state, marker) {
|
||||
return state.timingMarkers.get(marker) - state.requests.get("firstStartedMillis");
|
||||
return state.timingMarkers.get(marker) - state.requests.firstStartedMillis;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -8,7 +8,7 @@ const { REQUESTS_WATERFALL } = require("../constants");
|
||||
const { getDisplayedRequests } = require("./requests");
|
||||
|
||||
function isNetworkDetailsToggleButtonDisabled(state) {
|
||||
return getDisplayedRequests(state).isEmpty();
|
||||
return getDisplayedRequests(state).length == 0;
|
||||
}
|
||||
|
||||
const EPSILON = 0.001;
|
||||
|
Loading…
Reference in New Issue
Block a user