Bug 1745383 - [devtools] Adds basic functionality for the new HTTP Custom request panel r=bomsy

Depends on D135241

Differential Revision: https://phabricator.services.mozilla.com/D135039
This commit is contained in:
Claudia 2022-01-14 22:33:59 +00:00
Родитель f2fc43906e
Коммит 4650e8c828
7 изменённых файлов: 212 добавлений и 53 удалений

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

@ -8,15 +8,22 @@ const {
OPEN_ACTION_BAR,
SELECT_ACTION_BAR_TAB,
PANELS,
SEND_CUSTOM_REQUEST,
RIGHT_CLICK_REQUEST,
} = require("devtools/client/netmonitor/src/constants");
/**
* Open the entire HTTP Custom Request panel
* @returns {Function}
*/
function openHTTPCustomRequest() {
function openHTTPCustomRequest(isOpen) {
return ({ dispatch, getState }) => {
dispatch({ type: OPEN_ACTION_BAR, open: true });
dispatch({ type: OPEN_ACTION_BAR, open: isOpen });
dispatch({
type: SELECT_ACTION_BAR_TAB,
id: PANELS.HTTP_CUSTOM_REQUEST,
});
};
}
@ -32,6 +39,9 @@ function toggleHTTPCustomRequestPanel() {
state.ui.selectedActionBarTabId === PANELS.HTTP_CUSTOM_REQUEST;
dispatch({ type: OPEN_ACTION_BAR, open: !shouldClose });
// reset the right clicked request
dispatch({ type: RIGHT_CLICK_REQUEST, id: null });
dispatch({
type: SELECT_ACTION_BAR_TAB,
id: PANELS.HTTP_CUSTOM_REQUEST,
@ -39,7 +49,42 @@ function toggleHTTPCustomRequestPanel() {
};
}
/**
* Send a new HTTP request using the data in the custom request form.
*/
function sendHTTPCustomRequest(connector, request) {
return async ({ dispatch, getState }) => {
if (!request) {
return;
}
// Send a new HTTP request using the data in the custom request form
const data = {
cause: request.cause || {},
url: request.url,
method: request.method,
httpVersion: request.httpVersion,
};
if (request.headers) {
data.headers = request.headers.headers;
}
if (request.requestPostData) {
data.body = request.requestPostData.postData.text;
}
const { channelId } = await connector.sendHTTPRequest(data);
dispatch({
type: SEND_CUSTOM_REQUEST,
id: channelId,
});
};
}
module.exports = {
openHTTPCustomRequest,
toggleHTTPCustomRequestPanel,
sendHTTPCustomRequest,
};

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

@ -95,7 +95,7 @@ class NetworkActionBar extends Component {
title: L10N.getStr("netmonitor.actionbar.HTTPCustomRequest"),
className: "network-action-bar-HTTP-custom-request",
},
HTTPCustomRequestPanel()
HTTPCustomRequestPanel({ connector })
),
showSearchPanel &&
TabPanel(

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

@ -13,14 +13,13 @@ const {
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
const Actions = require("devtools/client/netmonitor/src/actions/index");
const {
getSelectedRequest,
getClickedRequest,
} = require("devtools/client/netmonitor/src/selectors/index");
const {
getUrlQuery,
parseQueryString,
writeHeaderText,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const { button, div, input, label, textarea } = dom;
const CUSTOM_HEADERS = L10N.getStr("netmonitor.custom.headers");
@ -42,37 +41,88 @@ const CUSTOM_SEND = L10N.getStr("netmonitor.custom.send");
class HTTPCustomRequestPanel extends Component {
static get propTypes() {
return {
connector: PropTypes.object,
connector: PropTypes.object.isRequired,
request: PropTypes.object,
sendCustomRequest: PropTypes.func.isRequired,
};
}
render() {
const { request = {}, sendCustomRequest } = this.props;
const {
method,
customQueryValue,
requestHeaders,
requestPostData,
url,
} = request;
constructor(props) {
super(props);
let headers = "";
if (requestHeaders) {
headers = requestHeaders.customHeadersValue
? requestHeaders.customHeadersValue
: writeHeaderText(requestHeaders.headers).trim();
const { request } = props;
this.state = {
method: request ? request.method : "",
url: request ? request.url : "",
headers: {
customHeadersValue: "",
headers: request ? request.requestHeaders.headers : [],
},
requestPostData: request
? request.requestPostData?.postData.text || ""
: "",
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleHeadersChange = this.handleHeadersChange.bind(this);
}
/**
* Parse a text representation of a name[divider]value list with
* the given name regex and divider character.
*
* @param {string} text - Text of list
* @return {array} array of headers info {name, value}
*/
parseRequestText(text, namereg, divider) {
const regex = new RegExp(`(${namereg})\\${divider}\\s*(\\S.*)`);
const pairs = [];
for (const line of text.split("\n")) {
const matches = regex.exec(line);
if (matches) {
const [, name, value] = matches;
pairs.push({ name, value });
}
}
return pairs;
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value,
});
}
handleHeadersChange(event) {
const target = event.target;
const value = target.value;
this.setState({
// Parse text representation of multiple HTTP headers
headers: {
customHeadersValue: value || "",
headers: this.parseRequestText(value, "\\S+?", ":"),
},
});
}
render() {
const { sendCustomRequest } = this.props;
const { method, requestPostData, url, headers } = this.state;
const formattedHeaders = headers.customHeadersValue
? headers.customHeadersValue
: writeHeaderText(headers.headers).trim();
const queryArray = url ? parseQueryString(getUrlQuery(url)) : [];
let params = customQueryValue;
if (!params) {
params = queryArray
? queryArray.map(({ name, value }) => name + "=" + value).join("\n")
: "";
}
const postData = requestPostData?.postData.text
? requestPostData.postData.text
const queryParams = queryArray
? queryArray.map(({ name, value }) => name + "=" + value).join("\n")
: "";
return div(
@ -87,7 +137,7 @@ class HTTPCustomRequestPanel extends Component {
{
className: "devtools-button",
id: "http-custom-request-send-button",
onClick: sendCustomRequest,
onClick: () => sendCustomRequest(this.state),
},
CUSTOM_SEND
)
@ -109,7 +159,8 @@ class HTTPCustomRequestPanel extends Component {
input({
className: "http-custom-method-value",
id: "http-custom-method-value",
onChange: evt => {},
name: "method",
onChange: this.handleInputChange,
onBlur: () => {},
value: method,
}),
@ -124,12 +175,13 @@ class HTTPCustomRequestPanel extends Component {
input({
className: "http-custom-url-value",
id: "http-custom-url-value",
onChange: evt => {},
name: "url",
onChange: this.handleInputChange,
value: url || "http://",
})
),
// Hide query field when there is no params
params
queryParams
? div(
{
className: "tabpanel-summary-container http-custom-section",
@ -145,9 +197,10 @@ class HTTPCustomRequestPanel extends Component {
textarea({
className: "tabpanel-summary-input",
id: "http-custom-query-value",
onChange: evt => {},
name: "queryParams",
onChange: () => {},
rows: 4,
value: params,
value: queryParams,
wrap: "off",
})
)
@ -167,9 +220,10 @@ class HTTPCustomRequestPanel extends Component {
textarea({
className: "tabpanel-summary-input",
id: "http-custom-headers-value",
onChange: evt => {},
name: "headers",
onChange: this.handleHeadersChange,
rows: 8,
value: headers,
value: formattedHeaders,
wrap: "off",
})
),
@ -188,9 +242,10 @@ class HTTPCustomRequestPanel extends Component {
textarea({
className: "tabpanel-summary-input",
id: "http-custom-postdata-value",
onChange: evt => {},
name: "requestPostData",
onChange: this.handleInputChange,
rows: 6,
value: postData,
value: requestPostData,
wrap: "off",
})
)
@ -200,9 +255,9 @@ class HTTPCustomRequestPanel extends Component {
}
module.exports = connect(
state => ({ request: getSelectedRequest(state) }),
state => ({ request: getClickedRequest(state) }),
(dispatch, props) => ({
sendCustomRequest: () =>
dispatch(Actions.sendCustomRequest(props.connector)),
sendCustomRequest: request =>
dispatch(Actions.sendHTTPCustomRequest(props.connector, request)),
})
)(HTTPCustomRequestPanel);

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

@ -5,6 +5,7 @@
"use strict";
const { createFactory } = require("devtools/client/shared/vendor/react");
const Services = require("Services");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const {
@ -48,10 +49,18 @@ function NetworkDetailsBar({
return null;
}
const newEditAndResendPref = Services.prefs.getBoolPref(
"devtools.netmonitor.features.newEditAndResend"
);
return div(
{ className: "network-details-bar" },
!request.isCustom
? TabboxPanel({
request.isCustom && !newEditAndResendPref
? CustomRequestPanel({
connector,
request,
})
: TabboxPanel({
activeTabId,
cloneSelectedRequest,
connector,
@ -63,10 +72,6 @@ function NetworkDetailsBar({
openNetworkDetails,
targetSearchResult,
})
: CustomRequestPanel({
connector,
request,
})
);
}

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

@ -78,7 +78,8 @@ class RequestListContent extends Component {
cloneRequest: PropTypes.func.isRequired,
clickedRequest: PropTypes.object,
openDetailsPanelTab: PropTypes.func.isRequired,
openDetailsHTTPCustomRequestTab: PropTypes.func.isRequired,
openHTTPCustomRequestTab: PropTypes.func.isRequired,
closeHTTPCustomRequestTab: PropTypes.func.isRequired,
sendCustomRequest: PropTypes.func.isRequired,
displayedRequests: PropTypes.array.isRequired,
firstRequestStartedMs: PropTypes.number.isRequired,
@ -342,7 +343,8 @@ class RequestListContent extends Component {
connector,
cloneRequest,
openDetailsPanelTab,
openDetailsHTTPCustomRequestTab,
openHTTPCustomRequestTab,
closeHTTPCustomRequestTab,
sendCustomRequest,
openStatistics,
openRequestBlockingAndAddUrl,
@ -353,7 +355,8 @@ class RequestListContent extends Component {
connector,
cloneRequest,
openDetailsPanelTab,
openDetailsHTTPCustomRequestTab,
openHTTPCustomRequestTab,
closeHTTPCustomRequestTab,
sendCustomRequest,
openStatistics,
openRequestBlockingAndAddUrl,
@ -464,8 +467,10 @@ module.exports = connect(
(dispatch, props) => ({
cloneRequest: id => dispatch(Actions.cloneRequest(id)),
openDetailsPanelTab: () => dispatch(Actions.openNetworkDetails(true)),
openDetailsHTTPCustomRequestTab: () =>
openHTTPCustomRequestTab: () =>
dispatch(Actions.openHTTPCustomRequest(true)),
closeHTTPCustomRequestTab: () =>
dispatch(Actions.openHTTPCustomRequest(false)),
sendCustomRequest: () =>
dispatch(Actions.sendCustomRequest(props.connector)),
openStatistics: open =>

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

@ -248,7 +248,8 @@ class RequestListContextMenu {
connector,
cloneRequest,
openDetailsPanelTab,
openDetailsHTTPCustomRequestTab,
openHTTPCustomRequestTab,
closeHTTPCustomRequestTab,
sendCustomRequest,
openStatistics,
openRequestInTab,
@ -332,7 +333,8 @@ class RequestListContextMenu {
cloneRequest(id);
openDetailsPanelTab();
} else {
openDetailsHTTPCustomRequestTab();
closeHTTPCustomRequestTab();
openHTTPCustomRequestTab();
}
});
},

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

@ -115,3 +115,50 @@ add_task(async function() {
await teardown(monitor);
});
/**
* Test if the content it is not connected to the current selection.
*/
add_task(async function() {
const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
requestCount: 1,
});
info("Starting test... ");
const { document, store, windowRequire } = monitor.panelWin;
// Action should be processed synchronously in tests.
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
await performRequests(monitor, tab, 2);
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
info("selecting first request");
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
info("opening the new request panel");
const waitForPanels = waitForDOM(
document,
".monitor-panel .network-action-bar"
);
getContextMenuItem(monitor, "request-list-context-resend").click();
await waitForPanels;
const urlValue = document.querySelector("http-custom-url-value");
const request = document.querySelectorAll(".request-list-item")[1];
EventUtils.sendMouseEvent({ type: "mousedown" }, request);
const urlValueChanged = document.querySelector("http-custom-url-value");
is(
urlValue,
urlValueChanged,
"The url should not change when click on a new request"
);
await teardown(monitor);
});