Bug 1758361 - [devtools] Fix crash when viewing request headers from upload stream r=nchevobbe

This patch fixes 2 crashes, for different reasons and improves/ adds tests to cover the issues
- The raw view of the `Request headers for upload stream` crashes when there
  is only one header sent. This is covered by the scenario

  ```
  [
    "content-type: application/x-www-form-urlencoded\r",
    "\r",
    "\r",
    "foo=bar&baz=123"
  ]
  ```

- The Request side panel crashes when there is no form data body content added to the payload sent.
  This is covered in the test by the scenario

  ```
  [
    "content-type: application/x-www-form-urlencoded\r",
    "\r",
    "\r",
  ]
  ```

Differential Revision: https://phabricator.services.mozilla.com/D143339
This commit is contained in:
Hubert Boma Manilla 2022-04-15 08:16:17 +00:00
Родитель 42b7d01933
Коммит 0a4230aa64
6 изменённых файлов: 194 добавлений и 96 удалений

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

@ -386,9 +386,11 @@ class HeadersPanel extends Component {
let rows;
if (value) {
const match = value.match(/\n/g);
rows = match !== null ? match.length : 0;
// Need to add 1 for the horizontal scrollbar
// not to cover the last row of raw data
rows = value.match(/\n/g).length + 1;
rows = rows + 1;
}
return tr(

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

@ -53,7 +53,7 @@ async function getFormDataSections(
const postDataLongString = postData.postData.text;
const text = await getLongString(postDataLongString);
for (const section of text.split(/\r\n|\r|\n/)) {
for (const section of text.trim().split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
@ -359,7 +359,7 @@ function parseQueryString(query) {
*/
function parseFormData(sections) {
if (!sections) {
return null;
return [];
}
return sections

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

@ -21,103 +21,180 @@ add_task(async function() {
store.dispatch(Actions.batchEnable(false));
// Execute requests.
await performRequests(monitor, tab, 1);
await performRequests(monitor, tab, 3);
// Wait for all tree view updated by react
let wait = waitForDOM(document, "#headers-panel .accordion-item", 3);
const expectedRequestsContent = [
{
headersFromUploadSectionTitle:
"Request headers from upload stream (47 B)",
uploadSectionHeaders: [
{ label: "content-type", value: "application/x-www-form-urlencoded" },
],
uploadSectionRawText: "content-type: application/x-www-form-urlencoded",
requestPanelFormData: [
{ label: "foo", value: '"bar"' },
{ label: "baz", value: '"123"' },
],
requestPanelPayload: [
"content-type: application/x-www-form-urlencoded",
"foo=bar&baz=123",
],
},
{
headersFromUploadSectionTitle:
"Request headers from upload stream (47 B)",
uploadSectionHeaders: [
{ label: "content-type", value: "application/x-www-form-urlencoded" },
],
uploadSectionRawText: "content-type: application/x-www-form-urlencoded",
requestPanelPayload: ["content-type: application/x-www-form-urlencoded"],
},
{
headersFromUploadSectionTitle:
"Request headers from upload stream (74 B)",
uploadSectionHeaders: [
{ label: "content-type", value: "application/x-www-form-urlencoded" },
{ label: "custom-header", value: "hello world!" },
],
uploadSectionRawText:
"content-type: application/x-www-form-urlencoded\r\ncustom-header: hello world!",
requestPanelFormData: [
{ label: "foo", value: '"bar"' },
{ label: "baz", value: '"123"' },
],
requestPanelPayload: [
"content-type: application/x-www-form-urlencoded",
"custom-header: hello world!",
"foo=bar&baz=123",
],
},
];
const requests = document.querySelectorAll(".request-list-item");
store.dispatch(Actions.toggleNetworkDetails());
clickOnSidebarTab(document, "headers");
await wait;
let tabpanel = document.querySelector("#headers-panel");
is(
tabpanel.querySelectorAll(".accordion-item").length,
3,
"There should be 3 header sections displayed in this tabpanel."
);
for (let i = 0; i < expectedRequestsContent.length; i++) {
EventUtils.sendMouseEvent({ type: "mousedown" }, requests[i]);
await assertRequestContentInHeaderAndRequestSidePanels(
expectedRequestsContent[i]
);
}
is(
tabpanel.querySelectorAll(".accordion-item .accordion-header-label")[2]
.textContent,
L10N.getStr("requestHeadersFromUpload") +
" (" +
L10N.getFormatStr("networkMenu.sizeB", 74) +
")",
"The request headers from upload section doesn't have the correct title."
);
async function assertRequestContentInHeaderAndRequestSidePanels(expected) {
// Wait for all 3 headers sections to load (Response Headers, Request Headers, Request headers from upload stream)
let wait = waitForDOM(document, "#headers-panel .accordion-item", 3);
clickOnSidebarTab(document, "headers");
await wait;
let labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
let values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
let tabpanel = document.querySelector("#headers-panel");
is(
tabpanel.querySelectorAll(".accordion-item").length,
3,
"There should be 3 header sections displayed in this tabpanel."
);
is(
labels[labels.length - 2].textContent,
"content-type",
"The first request header name was incorrect."
);
is(
values[values.length - 2].textContent,
"application/x-www-form-urlencoded",
"The first request header value was incorrect."
);
is(
labels[labels.length - 1].textContent,
"custom-header",
"The second request header name was incorrect."
);
is(
values[values.length - 1].textContent,
"hello world!",
"The second request header value was incorrect."
);
info("Check that the Headers in the upload stream section are correct.");
is(
tabpanel.querySelectorAll(".accordion-item .accordion-header-label")[2]
.textContent,
expected.headersFromUploadSectionTitle,
"The request headers from upload section doesn't have the correct title."
);
// Wait for raw data toggle to be displayed
wait = waitForDOM(
document,
"#request-panel .raw-data-toggle-input .devtools-checkbox-toggle"
);
clickOnSidebarTab(document, "request");
await wait;
let labels = tabpanel.querySelectorAll(
".accordion-item:last-child .accordion-content tr .treeLabelCell .treeLabel"
);
let values = tabpanel.querySelectorAll(
".accordion-item:last-child .accordion-content tr .treeValueCell .objectBox"
);
tabpanel = document.querySelector("#request-panel");
for (let i = 0; i < labels.length; i++) {
is(
labels[i].textContent,
expected.uploadSectionHeaders[i].label,
"The request header name was incorrect."
);
is(
values[i].textContent,
expected.uploadSectionHeaders[i].value,
"The request header value was incorrect."
);
}
ok(
tabpanel.querySelector(".treeTable"),
"The params tree view should be displayed."
);
ok(
tabpanel.querySelector(".editor-mount") === null,
"The post data shouldn't be displayed."
);
info(
"Toggle to open the raw view for the request headers from upload stream"
);
is(
tabpanel.querySelector(".data-label").textContent,
L10N.getStr("paramsFormData"),
"The form data section doesn't have the correct title."
);
wait = waitForDOM(
tabpanel,
".accordion-item:last-child .accordion-content .raw-headers-container"
);
tabpanel.querySelector("#raw-upload-checkbox").click();
await wait;
labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
const rawTextArea = tabpanel.querySelector(
".accordion-item:last-child .accordion-content .raw-headers"
);
is(
rawTextArea.textContent,
expected.uploadSectionRawText,
"The raw text for the request headers from upload section is correct"
);
is(
labels[0].textContent,
"foo",
"The first payload param name was incorrect."
);
is(
values[0].textContent,
`"bar"`,
"The first payload param value was incorrect."
);
is(
labels[1].textContent,
"baz",
"The second payload param name was incorrect."
);
is(
values[1].textContent,
`"123"`,
"The second payload param value was incorrect."
);
info("Switch to the Request panel");
wait = waitForDOM(document, "#request-panel .panel-container");
clickOnSidebarTab(document, "request");
await wait;
tabpanel = document.querySelector("#request-panel");
if (expected.requestPanelFormData) {
await waitUntil(
() =>
tabpanel.querySelector(".data-label").textContent ==
L10N.getStr("paramsFormData")
);
labels = tabpanel.querySelectorAll("tr .treeLabelCell .treeLabel");
values = tabpanel.querySelectorAll("tr .treeValueCell .objectBox");
for (let i = 0; i < labels.length; i++) {
is(
labels[i].textContent,
expected.requestPanelFormData[i].label,
"The form data param name was incorrect."
);
is(
values[i].textContent,
expected.requestPanelFormData[i].value,
"The form data param value was incorrect."
);
}
info("Toggle open the the request payload raw view");
tabpanel.querySelector("#raw-request-checkbox").click();
}
await waitUntil(
() =>
tabpanel.querySelector(".data-label").textContent ==
L10N.getStr("paramsPostPayload") &&
tabpanel.querySelector(
".panel-container .editor-row-container .CodeMirror-code"
)
);
// Check that the expected header lines are included in the codemirror
// text.
const actualText = tabpanel.querySelector(
".panel-container .editor-row-container .CodeMirror-code"
).textContent;
const requestPayloadIsCorrect = expected.requestPanelPayload.every(
content => actualText.includes(content)
);
is(requestPayloadIsCorrect, true, "The request payload is not correct");
}
return teardown(monitor);
});

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

@ -41,7 +41,7 @@ add_task(async function() {
const waitForPanel = waitForDOM(
document,
"#stack-trace-panel .frame-link",
5
6
);
// Open the stack-trace tab for that request
document.getElementById("stack-trace-tab").click();

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

@ -27,7 +27,7 @@
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
resolve();
}
}
};
xhr.send(message);
});

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

@ -34,13 +34,32 @@
async function performRequests() {
const rawData = [
"content-type: application/x-www-form-urlencoded\r",
"custom-header: hello world!\r",
"\r",
"\r",
"foo=bar&baz=123",
// Only one header
[
"content-type: application/x-www-form-urlencoded\r",
"\r",
"\r",
"foo=bar&baz=123",
],
// No form body content
[
"content-type: application/x-www-form-urlencoded\r",
"\r",
"\r",
],
// Multiple headers
[
"content-type: application/x-www-form-urlencoded\r",
"custom-header: hello world!\r",
"\r",
"\r",
"foo=bar&baz=123",
],
];
await post("sjs_simple-test-server.sjs", rawData.join("\n"));
for (const data of rawData) {
await post("sjs_simple-test-server.sjs", data.join("\n"));
}
}
</script>
</body>