зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f2fc43906e
Коммит
4650e8c828
|
@ -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);
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче