Bug 1495797 - change Raw headers buttons, plus styling changes. r=honza
authorlenka <lpelechova@mozilla.com>
Thu, 10 Jan 2019 13:59:17 +0100
changeset 514823 fb87ba30098c91ee7f8a3b8c59b9e344d3a5525a
parent 514822 7a6c6a3752664e1bfa0f02d0f58c8f03a9c77b41
child 514824 366f545cfcb4b1f7243d0fc3d8d6b02e7832b52d
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonza
bugs1495797
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1495797 - change Raw headers buttons, plus styling changes. r=honza Additional work for display of raw upload headers, plus corrected tests
devtools/client/netmonitor/src/assets/styles/NetworkDetailsPanel.css
devtools/client/netmonitor/src/components/HeadersPanel.js
devtools/client/netmonitor/test/browser_net_headers_sorted.js
devtools/client/netmonitor/test/browser_net_raw_headers.js
devtools/client/shared/components/MdnLink.css
--- a/devtools/client/netmonitor/src/assets/styles/NetworkDetailsPanel.css
+++ b/devtools/client/netmonitor/src/assets/styles/NetworkDetailsPanel.css
@@ -103,17 +103,18 @@
 }
 
 /* Make right td fill available horizontal space */
 .network-monitor .tree-container .treeTable td:last-child {
   width: 100%;
 }
 
 .network-monitor .properties-view .devtools-searchbox,
-.network-monitor .tree-container .treeTable .tree-section {
+.network-monitor .tree-container .treeTable .tree-section,
+.network-monitor .properties-view .raw-headers-container  {
   width: 100%;
   background-color: var(--theme-toolbar-background);
 }
 
 .network-monitor .tree-container .treeTable tr.tree-section:not(:first-child) td:not([class=""]) {
   border-top: 1px solid var(--theme-splitter-color);
 }
 
@@ -202,51 +203,114 @@
   color: inherit;
   padding-inline-start: 3px;
 }
 
 .theme-dark .network-monitor .tabpanel-summary-value {
   color: var(--theme-selection-color);
 }
 
+.theme-dark .network-monitor .edit-and-resend-button  {
+  background-color: var(--toolbarbutton-background);
+  color: var(--theme-selection-color);
+}
+
+.summary-edit-and-resend {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  padding-inline-end: 3px;
+}
+
 /* Headers tabpanel */
 
 .network-monitor .headers-overview {
   background: var(--theme-toolbar-background);
 }
 
 .network-monitor .headers-summary,
 .network-monitor .response-summary {
   display: flex;
   align-items: center;
 }
 
-.network-monitor .headers-summary .devtools-button {
-  margin-inline-end: 6px;
+.theme-light .network-monitor .edit-and-resend-button {
+  background-color: var(--grey-20);
 }
 
-.network-monitor .headers-summary .raw-headers-container {
+.network-monitor .edit-and-resend-button {
+  align-self: flex-end;
+  height: 24px;
+  padding-left: 8px;
+  padding-right: 8px;
+  width: auto;
+  border: 1px solid var(--theme-splitter-color);
+}
+
+.network-monitor .raw-headers-toggle {
   display: flex;
-  width: 100%;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  justify-content: flex-end;
+  align-items: center;
+}
+
+.network-monitor .raw-headers-toggle .headers-summary-label {
+  color: var(--theme-toolbar-color);
 }
 
-.network-monitor .headers-summary .raw-headers {
-  width: 50%;
-  padding: 0 4px;
+.network-monitor .raw-headers-toggle-input > input {
+  display: inline-block;
+  align-items: center;
+  background: var(--toggle-track-color);
+  width: 2em ;
+  vertical-align: bottom;
+}
+
+.network-monitor .properties-view .tree-container .treeTable .treeValueCell .devtools-checkbox-toggle {
+  margin-top: 2px;
+  margin-bottom: 2px;
 }
 
-.network-monitor .headers-summary .raw-headers textarea {
+.network-monitor .properties-view .raw-headers-container .raw-headers {
+  display: block;
+  overflow: hidden;
   width: 100%;
-  height: 50vh;
-  font: message-box;
+  padding: 2px 12px;
+  white-space: pre;
+  overflow-wrap: normal;
+  overflow-x: auto;
+  border: none;
+}
+
+.network-monitor .properties-view .raw-headers-container td {
+  display: block;
+}
+
+.network-monitor .properties-view .raw-headers-container textarea {
+  width: 100%;
+  font-family: var(--monospace-font-family);
   font-size: var(--theme-body-font-size);
   resize: none;
 }
 
-.network-monitor .headers-summary .raw-headers .tabpanel-summary-label {
+.theme-light .network-monitor .properties-view textarea {
+  background-color: white;
+  border: 1px solid var(--grey-25);
+  color: var(--grey-90);
+}
+
+.theme-dark .network-monitor .properties-view textarea {
+  background-color: var(--grey-70);
+  border: 1px solid var(--grey-85);
+  color: white;
+}
+
+.network-monitor .properties-view .raw-headers .tabpanel-summary-label {
   padding: 0 0 4px 0;
 }
 
 .headers-summary .textbox-input {
   margin-inline-end: 2px;
 }
 
 .network-monitor .headers-summary .status-text {
--- a/devtools/client/netmonitor/src/components/HeadersPanel.js
+++ b/devtools/client/netmonitor/src/components/HeadersPanel.js
@@ -33,23 +33,24 @@ loader.lazyGetter(this, "MDNLink", funct
   return createFactory(require("devtools/client/shared/components/MdnLink"));
 });
 loader.lazyGetter(this, "Rep", function() {
   return require("devtools/client/shared/components/reps/reps").REPS.Rep;
 });
 loader.lazyGetter(this, "MODE", function() {
   return require("devtools/client/shared/components/reps/reps").MODE;
 });
+loader.lazyGetter(this, "TreeRow", function() {
+  return createFactory(require("devtools/client/shared/components/tree/TreeRow"));
+});
 
-const { button, div, input, textarea, span } = dom;
+const { button, div, input, label, span, textarea, tr, td } = dom;
 
 const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");
 const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders");
-const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders");
-const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders");
 const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText");
 const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText");
 const REQUEST_HEADERS = L10N.getStr("requestHeaders");
 const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload");
 const RESPONSE_HEADERS = L10N.getStr("responseHeaders");
 const SUMMARY_ADDRESS = L10N.getStr("netmonitor.summary.address");
 const SUMMARY_METHOD = L10N.getStr("netmonitor.summary.method");
 const SUMMARY_URL = L10N.getStr("netmonitor.summary.url");
@@ -62,32 +63,38 @@ const SUMMARY_REFERRER_POLICY = L10N.get
  * Headers panel component
  * Lists basic information about the request
  */
 class HeadersPanel extends Component {
   static get propTypes() {
     return {
       connector: PropTypes.object.isRequired,
       cloneSelectedRequest: PropTypes.func.isRequired,
+      member: PropTypes.object.isRequired,
       request: PropTypes.object.isRequired,
       renderValue: PropTypes.func,
       openLink: PropTypes.func,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
-      rawHeadersOpened: false,
+      rawRequestHeadersOpened: false,
+      rawResponseHeadersOpened: false,
+      rawUploadHeadersOpened: false,
     };
 
     this.getProperties = this.getProperties.bind(this);
-    this.toggleRawHeaders = this.toggleRawHeaders.bind(this);
+    this.toggleRawResponseHeaders = this.toggleRawResponseHeaders.bind(this);
+    this.toggleRawRequestHeaders = this.toggleRawRequestHeaders.bind(this);
+    this.toggleRawUploadHeaders = this.toggleRawUploadHeaders.bind(this);
     this.renderSummary = this.renderSummary.bind(this);
+    this.renderRow = this.renderRow.bind(this);
     this.renderValue = this.renderValue.bind(this);
   }
 
   componentDidMount() {
     const { request, connector } = this.props;
     fetchNetworkUpdatePacket(connector.requestData, request, [
       "requestHeaders",
       "responseHeaders",
@@ -100,51 +107,220 @@ class HeadersPanel extends Component {
     fetchNetworkUpdatePacket(connector.requestData, request, [
       "requestHeaders",
       "responseHeaders",
       "requestPostData",
     ]);
   }
 
   getProperties(headers, title) {
+    let propertiesResult;
+
     if (headers && headers.headers.length) {
       const headerKey = `${title} (${getFormattedSize(headers.headersSize, 3)})`;
-      const propertiesResult = {
+
+      propertiesResult = {
         [headerKey]: new HeaderList(headers.headers),
       };
-      return propertiesResult;
+
+      if ((title === RESPONSE_HEADERS && this.state.rawResponseHeadersOpened) ||
+          (title === REQUEST_HEADERS && this.state.rawRequestHeadersOpened) ||
+          (title === REQUEST_HEADERS_FROM_UPLOAD && this.state.rawUploadHeadersOpened)) {
+        propertiesResult = {
+          [headerKey]: { RAW_HEADERS_ID: headers.rawHeaders },
+        };
+      }
     }
-
-    return null;
+    return propertiesResult;
   }
 
-  toggleRawHeaders() {
+  toggleRawResponseHeaders() {
     this.setState({
-      rawHeadersOpened: !this.state.rawHeadersOpened,
+      rawResponseHeadersOpened: !this.state.rawResponseHeadersOpened,
     });
   }
 
-  renderSummary(label, value) {
+  toggleRawRequestHeaders() {
+    this.setState({
+      rawRequestHeadersOpened: !this.state.rawRequestHeadersOpened,
+    });
+  }
+
+  toggleRawUploadHeaders() {
+    this.setState({
+      rawUploadHeadersOpened: !this.state.rawUploadHeadersOpened,
+    });
+  }
+
+  /**
+   * Helper method to identify what kind of raw header this is.
+   * Information is in the path variable
+   */
+  getRawHeaderType(path) {
+    if (path.includes(RESPONSE_HEADERS)) {
+      return "RESPONSE";
+    }
+    if (path.includes(REQUEST_HEADERS_FROM_UPLOAD)) {
+      return "UPLOAD";
+    }
+    return "REQUEST";
+  }
+
+  /**
+   * Renders the top part of the headers detail panel - Summary.
+   */
+  renderSummary(summaryLabel, value) {
     return (
       div({ className: "tabpanel-summary-container headers-summary" },
         div({ className: "tabpanel-summary-labelvalue"},
           span({ className: "tabpanel-summary-label headers-summary-label"},
-            label
+          summaryLabel
           ),
           span({ className: "tabpanel-summary-value textbox-input devtools-monospace"},
             value
           ),
         ),
       )
     );
   }
 
+  /**
+   * Custom rendering method passed to PropertiesView. It's responsible
+   * for rendering <textarea> element with raw headers data.
+   */
+  renderRow(props) {
+    const {
+      level,
+      path,
+    } = props.member;
+
+    const {
+      request: {
+        httpVersion,
+        requestHeaders,
+        requestHeadersFromUploadStream: uploadHeaders,
+        responseHeaders,
+        status,
+        statusText,
+      },
+    } = this.props;
+
+    let value;
+
+    if (level === 1 && path.includes("RAW_HEADERS_ID")) {
+      const rawHeaderType = this.getRawHeaderType(path);
+      switch (rawHeaderType) {
+        case "REQUEST":
+          value = writeHeaderText(requestHeaders.headers);
+          break;
+        case "RESPONSE":
+          // display Status-Line above other response headers
+          const statusLine = `${httpVersion} ${status} ${statusText}\n`;
+          value = statusLine + writeHeaderText(responseHeaders.headers);
+          break;
+        case "UPLOAD":
+          value = writeHeaderText(uploadHeaders.headers);
+          break;
+      }
+
+      let rows;
+      if (value) {
+        // Need to add 1 for the horizontal scrollbar
+        // not to cover the last row of raw data
+        rows = value.match(/\n/g).length + 1;
+      }
+
+      return (
+        tr({
+          key: path,
+          role: "treeitem",
+          className: "raw-headers-container",
+        },
+          td({
+            colSpan: 2,
+          },
+            textarea({
+              className: "raw-headers",
+              rows: rows,
+              value: value,
+              readOnly: true,
+            })
+          )
+        )
+      );
+    }
+
+    return TreeRow(props);
+  }
+
+  /**
+   * Rendering toggle buttons for switching between formated and raw
+   * headers data.
+   */
+  renderInput(onChange, checked) {
+    return (
+     input({
+       checked,
+       className: "devtools-checkbox-toggle",
+       onChange,
+       type: "checkbox",
+     })
+    );
+  }
+
+  renderToggleRawHeadersBtn(path) {
+    let inputElement;
+
+    const rawHeaderType = this.getRawHeaderType(path);
+    switch (rawHeaderType) {
+      case "REQUEST":
+        // Render toggle button for REQUEST header
+        inputElement =
+        this.renderInput(
+          this.toggleRawRequestHeaders, this.state.rawRequestHeadersOpened);
+        break;
+      case "RESPONSE":
+        // Render toggle button for RESPONSE header
+        inputElement = this.renderInput(
+          this.toggleRawResponseHeaders, this.state.rawResponseHeadersOpened);
+        break;
+      case "UPLOAD":
+        // Render toggle button for UPLOAD header
+        inputElement =
+        this.renderInput(
+          this.toggleRawUploadHeaders, this.state.rawUploadHeadersOpened);
+        break;
+    }
+
+    return (
+      label({ className: "raw-headers-toggle" },
+        span({ className: "headers-summary-label"},
+          RAW_HEADERS
+        ),
+        div({ className: "raw-headers-toggle-input" },
+          inputElement
+        )
+      )
+    );
+  }
+
   renderValue(props) {
     const member = props.member;
     const value = props.value;
+    const path = member.path;
+    let toggleRawHeadersBtn;
+
+    // When member.level === 0, it is a section label
+    // Request/Response header
+    if (member.level === 0) {
+      toggleRawHeadersBtn = this.renderToggleRawHeadersBtn(path);
+
+      // Return label and toggle button
+      return toggleRawHeadersBtn;
+    }
 
     if (typeof value !== "string") {
       return null;
     }
 
     const headerDocURL = getHeadersURL(member.name);
 
     return (
@@ -211,20 +387,17 @@ class HeadersPanel extends Component {
       this.renderSummary(SUMMARY_ADDRESS,
         getFormattedIPAndPort(remoteAddress, remotePort)) : null;
 
     let summaryStatus;
 
     if (status) {
       const statusCodeDocURL = getHTTPStatusCodeURL(status.toString());
       const inputWidth = statusText.length + 1;
-      const toggleRawHeadersClassList = ["devtools-button", "raw-headers-button"];
-      if (this.state.rawHeadersOpened) {
-        toggleRawHeadersClassList.push("checked");
-      }
+
       summaryStatus = (
         div({ className: "tabpanel-summary-container headers-summary" },
           div({
             className: "tabpanel-summary-label headers-summary-label",
           }, SUMMARY_STATUS),
           StatusCode({ item }),
           input({
             className: "tabpanel-summary-value textbox-input devtools-monospace"
@@ -234,80 +407,53 @@ class HeadersPanel extends Component {
             size: `${inputWidth}`,
           }),
           statusCodeDocURL ? MDNLink({
             url: statusCodeDocURL,
             title: SUMMARY_STATUS_LEARN_MORE,
           }) : span({
             className: "headers-summary learn-more-link",
           }),
-          button({
-            className: "devtools-button edit-and-resend-button",
-            onClick: cloneSelectedRequest,
-          }, EDIT_AND_RESEND),
-          button({
-            "aria-pressed": this.state.rawHeadersOpened,
-            className: toggleRawHeadersClassList.join(" "),
-            onClick: this.toggleRawHeaders,
-          }, RAW_HEADERS),
         )
       );
     }
 
     const summaryVersion = httpVersion ?
       this.renderSummary(SUMMARY_VERSION, httpVersion) : null;
 
     const summaryReferrerPolicy = referrerPolicy ?
       this.renderSummary(SUMMARY_REFERRER_POLICY, referrerPolicy) : null;
 
-    // display Status-Line above other response headers
-    const statusLine = `${httpVersion} ${status} ${statusText}\n`;
-
-    let summaryRawHeaders;
-    if (this.state.rawHeadersOpened) {
-      summaryRawHeaders = (
-        div({ className: "tabpanel-summary-container headers-summary" },
-          div({ className: "raw-headers-container" },
-            div({ className: "raw-headers" },
-              div({ className: "tabpanel-summary-label" }, RAW_HEADERS_REQUEST),
-              textarea({
-                className: "raw-request-headers-textarea",
-                value: writeHeaderText(requestHeaders.headers),
-                readOnly: true,
-              }),
-            ),
-            div({ className: "raw-headers" },
-              div({ className: "tabpanel-summary-label" }, RAW_HEADERS_RESPONSE),
-              textarea({
-                className: "raw-response-headers-textarea",
-                value: statusLine + writeHeaderText(responseHeaders.headers),
-                readOnly: true,
-              }),
-            ),
-          )
-        )
-      );
-    }
+    const summaryEditAndResendBtn = (
+      div({
+        className: "summary-edit-and-resend" },
+        summaryReferrerPolicy,
+        button({
+          className: "edit-and-resend-button devtools-button",
+          onClick: cloneSelectedRequest,
+        }, EDIT_AND_RESEND)
+      )
+    );
 
     return (
       div({ className: "panel-container" },
         div({ className: "headers-overview" },
           summaryUrl,
           summaryMethod,
           summaryAddress,
           summaryStatus,
           summaryVersion,
-          summaryReferrerPolicy,
-          summaryRawHeaders,
+          summaryEditAndResendBtn,
         ),
         PropertiesView({
           object,
           provider: HeadersProvider,
           filterPlaceHolder: HEADERS_FILTER_TEXT,
           sectionNames: Object.keys(object),
+          renderRow: this.renderRow,
           renderValue: this.renderValue,
           openLink,
         }),
       )
     );
   }
 }
 
--- a/devtools/client/netmonitor/test/browser_net_headers_sorted.js
+++ b/devtools/client/netmonitor/test/browser_net_headers_sorted.js
@@ -84,30 +84,35 @@ async function verifyRawHeaders(monitor)
                                    "foo-bar", "foo-bar", "connection", "server",
                                    "date", "content-length"];
 
   const expectedRequestHeaders = ["Host", "User-Agent", "Accept", "Accept-Language",
                                   "Accept-Encoding", "Connection", "Cookie",
                                   "Upgrade-Insecure-Requests", "Pragma",
                                   "Cache-Control"];
 
-  // Click the 'Raw headers' button to show original headers source.
-  const rawHeadersBtn = document.querySelector(".raw-headers-button");
-  rawHeadersBtn.click();
+  // Click the 'Raw headers' toggle to show original headers source.
+  for (const rawToggleInput of document.querySelectorAll(".devtools-checkbox-toggle")) {
+    rawToggleInput.click();
+  }
 
   // Wait till raw headers are available.
+  let rawArr;
   await waitUntil(() => {
-    return document.querySelector(".raw-request-headers-textarea") &&
-      document.querySelector(".raw-response-headers-textarea");
+    rawArr = document.querySelectorAll("textarea.raw-headers");
+    // Both raw headers must be present
+    return (rawArr.length > 1);
   });
 
+  // Request headers are rendered first, so it is element with index 1
   const requestHeadersText =
-    document.querySelector(".raw-request-headers-textarea").textContent;
+    rawArr[1].textContent;
+  // Response headers are rendered first, so it is element with index 0
   const responseHeadersText =
-    document.querySelector(".raw-response-headers-textarea").textContent;
+    rawArr[0].textContent;
 
   const rawRequestHeadersArray = requestHeadersText.split("\n");
   for (let i = 0; i < rawRequestHeadersArray.length; i++) {
     const header = rawRequestHeadersArray[i];
     actualRequestHeaders.push(header.split(":")[0]);
   }
 
   const rawResponseHeadersArray = responseHeadersText.split("\n");
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js
@@ -22,66 +22,71 @@ add_task(async function() {
   // Execute requests.
   await performRequests(monitor, tab, 2);
 
   wait = waitForDOM(document, "#headers-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   await wait;
 
-  wait = waitForDOM(document, ".raw-headers-container textarea", 2);
-  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
+  wait = waitForDOM(document, "textarea.raw-headers", 2);
+  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersToggle("RESPONSE"));
+  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersToggle("REQUEST"));
   await wait;
 
-  testRawHeaderButtonStyle(true);
+  testRawHeaderToggleStyle(true);
 
   testShowRawHeaders(getSortedRequests(store.getState()).get(0));
 
-  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
+  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersToggle("RESPONSE"));
+  EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersToggle("REQUEST"));
 
-  testRawHeaderButtonStyle(false);
+  testRawHeaderToggleStyle(false);
 
   testHideRawHeaders(document);
 
   return teardown(monitor);
 
   /**
-   * Tests that checked, aria-pressed style is applied correctly
+   * Tests that checked is applied correctly
    *
    * @param checked
-   *        flag indicating whether button is pressed or not
+   *        flag indicating whether toggle is checked or not
    */
-  function testRawHeaderButtonStyle(checked) {
-    const rawHeadersButton = getRawHeadersButton();
+  function testRawHeaderToggleStyle(checked) {
+    const rawHeadersRequestToggle = getRawHeadersToggle("REQUEST");
+    const rawHeadersResponseToggle = getRawHeadersToggle("RESPONSE");
 
     if (checked) {
-      is(rawHeadersButton.classList.contains("checked"), true,
-        "The 'Raw Headers' button should have a 'checked' class.");
-      is(rawHeadersButton.getAttribute("aria-pressed"), "true",
-        "The 'Raw Headers' button should have the 'aria-pressed' attribute set to true");
+      is(rawHeadersRequestToggle.checked, true,
+        "The 'Raw Request Headers' toggle should be 'checked'");
+      is(rawHeadersResponseToggle.checked, true,
+        "The 'Raw Response Headers' toggle should be 'checked'");
     } else {
-      is(rawHeadersButton.classList.contains("checked"), false,
-        "The 'Raw Headers' button should not have a 'checked' class.");
-      is(rawHeadersButton.getAttribute("aria-pressed"), "false",
-        "The 'Raw Headers' button should have the 'aria-pressed' attribute set to false");
+      is(rawHeadersRequestToggle.checked, false,
+        "The 'Raw Request Headers' toggle should NOT be 'checked'");
+      is(rawHeadersResponseToggle.checked, false,
+        "The 'Raw Response Headers' toggle should NOT be 'checked'");
     }
   }
 
   /*
    * Tests that raw headers were displayed correctly
    */
   function testShowRawHeaders(data) {
+    // Request headers are rendered first, so it is element with index 1
     const requestHeaders = document
-      .querySelectorAll(".raw-headers-container textarea")[0].value;
+      .querySelectorAll("textarea.raw-headers")[1].value;
     for (const header of data.requestHeaders.headers) {
       ok(requestHeaders.includes(header.name + ": " + header.value),
         "textarea contains request headers");
     }
+    // Response headers are rendered first, so it is element with index 0
     const responseHeaders = document
-      .querySelectorAll(".raw-headers-container textarea")[1].value;
+      .querySelectorAll("textarea.raw-headers")[0].value;
     for (const header of data.responseHeaders.headers) {
       ok(responseHeaders.includes(header.name + ": " + header.value),
         "textarea contains response headers");
     }
   }
 
   /*
    * Tests that raw headers textareas are hidden
@@ -89,12 +94,16 @@ add_task(async function() {
   function testHideRawHeaders() {
     ok(!document.querySelector(".raw-headers-container"),
       "raw request headers textarea is empty");
   }
 
   /**
    * Returns the 'Raw Headers' button
    */
-  function getRawHeadersButton() {
-    return document.querySelectorAll(".headers-summary .devtools-button")[2];
+  function getRawHeadersToggle(rawHeaderType) {
+    if (rawHeaderType === "RESPONSE") {
+      // Response header is first displayed
+      return document.querySelectorAll(".devtools-checkbox-toggle")[0];
+    }
+    return document.querySelectorAll(".devtools-checkbox-toggle")[1];
   }
 });
--- a/devtools/client/shared/components/MdnLink.css
+++ b/devtools/client/shared/components/MdnLink.css
@@ -12,10 +12,10 @@
 .network-monitor .tree-container .treeTable tr .learn-more-link {
   position: absolute;
   top: 0;
   left: 0;
   padding: 0;
 }
 
 .network-monitor .tree-container .treeTable tr:not(:hover) .learn-more-link {
-  opacity: 0.1;
+  opacity: 0.4;
 }