[HeadersView] Enforce using lower case header names internally

Drive-by: header value is of type string|null

Bug: 1297533
Change-Id: I6f59658ba846a5da223037da66ea704fe09ae4db
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3870495
Commit-Queue: Wolfgang Beyer <[email protected]>
Reviewed-by: Simon Zünd <[email protected]>
diff --git a/front_end/panels/network/components/HeaderSectionRow.ts b/front_end/panels/network/components/HeaderSectionRow.ts
index ab902ee..4f8a8b9 100644
--- a/front_end/panels/network/components/HeaderSectionRow.ts
+++ b/front_end/panels/network/components/HeaderSectionRow.ts
@@ -79,7 +79,7 @@
           class="header-value ${this.#header.headerValueIncorrect ? 'header-warning' : ''}"
           @copy=${():void => Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue)}
         >
-          ${this.#header.value?.toString() || ''}
+          ${this.#header.value || ''}
           ${this.#maybeRenderHeaderValueSuffix(this.#header)}
         </div>
       </div>
@@ -89,9 +89,7 @@
   }
 
   #maybeRenderHeaderValueSuffix(header: HeaderDescriptor): LitHtml.LitTemplate {
-    const headerId = header.name.toLowerCase();
-
-    if (headerId === 'set-cookie' && header.setCookieBlockedReasons) {
+    if (header.name === 'set-cookie' && header.setCookieBlockedReasons) {
       const titleText =
           header.setCookieBlockedReasons.map(SDK.NetworkRequest.setCookieBlockedReasonToUiString).join('\n');
       // Disabled until https://crbug.com/1079231 is fixed.
@@ -107,8 +105,8 @@
       // clang-format on
     }
 
-    if (headerId === 'x-client-data') {
-      const data = ClientVariations.parseClientVariations(header.value?.toString() || '');
+    if (header.name === 'x-client-data') {
+      const data = ClientVariations.parseClientVariations(header.value || '');
       const output = ClientVariations.formatClientVariations(
           data, i18nString(UIStrings.activeClientExperimentVariation),
           i18nString(UIStrings.activeClientExperimentVariationIds));
@@ -206,8 +204,8 @@
 }
 
 export interface HeaderDescriptor {
-  name: string;
-  value: Object|null;
+  name: string;  // Headername should always be lower case.
+  value: string|null;
   headerValueIncorrect?: boolean|null;
   blockedDetails?: BlockedDetailsDescriptor;
   headerNotSet: boolean|null;
diff --git a/front_end/panels/network/components/RequestHeaderSection.ts b/front_end/panels/network/components/RequestHeaderSection.ts
index 058939f..6fafdca 100644
--- a/front_end/panels/network/components/RequestHeaderSection.ts
+++ b/front_end/panels/network/components/RequestHeaderSection.ts
@@ -57,16 +57,14 @@
   set data(data: RequestHeaderSectionData) {
     this.#request = data.request;
 
-    this.#headers = this.#request.requestHeaders().map(header => ({...header, headerNotSet: false}));
-    this.#headers.sort(function(a, b) {
-      return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase());
-    });
+    this.#headers = this.#request.requestHeaders().map(
+        header => ({name: header.name.toLowerCase(), value: header.value, headerNotSet: false}));
+    this.#headers.sort((a, b) => Platform.StringUtilities.compare(a.name, b.name));
 
     if (data.toReveal?.section === NetworkForward.UIRequestLocation.UIHeaderSection.Request) {
-      this.#headers.filter(header => header.name.toUpperCase() === data.toReveal?.header?.toUpperCase())
-          .forEach(header => {
-            header.highlight = true;
-          });
+      this.#headers.filter(header => header.name === data.toReveal?.header?.toLowerCase()).forEach(header => {
+        header.highlight = true;
+      });
     }
 
     this.#render();
diff --git a/front_end/panels/network/components/RequestHeadersView.ts b/front_end/panels/network/components/RequestHeadersView.ts
index 88431db..fc80357 100644
--- a/front_end/panels/network/components/RequestHeadersView.ts
+++ b/front_end/panels/network/components/RequestHeadersView.ts
@@ -425,7 +425,7 @@
 
   #renderGeneralRow(name: Common.UIString.LocalizedString, value: string, classNames?: string[]): LitHtml.LitTemplate {
     const isHighlighted = this.#toReveal?.section === NetworkForward.UIRequestLocation.UIHeaderSection.General &&
-        name.toUpperCase() === this.#toReveal?.header?.toUpperCase();
+        name.toLowerCase() === this.#toReveal?.header?.toLowerCase();
     return html`
       <div class="row ${isHighlighted ? 'header-highlight' : ''}">
         <div class="header-name">${name}:</div>
diff --git a/front_end/panels/network/components/ResponseHeaderSection.ts b/front_end/panels/network/components/ResponseHeaderSection.ts
index e2dd874..ef79440 100644
--- a/front_end/panels/network/components/ResponseHeaderSection.ts
+++ b/front_end/panels/network/components/ResponseHeaderSection.ts
@@ -99,7 +99,7 @@
     }
 
     function mergeHeadersWithIssues(
-        headers: SDK.NetworkRequest.NameValue[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
+        headers: HeaderDescriptor[], headersWithIssues: HeaderDescriptor[]): HeaderDescriptor[] {
       let i = 0, j = 0;
       const result: HeaderDescriptor[] = [];
       while (i < headers.length && j < headersWithIssues.length) {
@@ -120,14 +120,16 @@
       return result;
     }
 
-    this.#headers = mergeHeadersWithIssues(this.#request.sortedResponseHeaders.slice(), headersWithIssues);
+    this.#headers = this.#request.sortedResponseHeaders.map(
+        header => ({name: header.name.toLowerCase(), value: header.value, headerNotSet: false}));
+    this.#headers = mergeHeadersWithIssues(this.#headers, headersWithIssues);
 
     const blockedResponseCookies = this.#request.blockedResponseCookies();
     const blockedCookieLineToReasons = new Map<string, Protocol.Network.SetCookieBlockedReason[]>(
         blockedResponseCookies?.map(c => [c.cookieLine, c.blockedReasons]));
     for (const header of this.#headers) {
-      if (header.name.toLowerCase() === 'set-cookie' && header.value) {
-        const matchingBlockedReasons = blockedCookieLineToReasons.get(header.value.toString());
+      if (header.name === 'set-cookie' && header.value) {
+        const matchingBlockedReasons = blockedCookieLineToReasons.get(header.value);
         if (matchingBlockedReasons) {
           header.setCookieBlockedReasons = matchingBlockedReasons;
         }
@@ -135,10 +137,9 @@
     }
 
     if (data.toReveal?.section === NetworkForward.UIRequestLocation.UIHeaderSection.Response) {
-      this.#headers.filter(header => header.name.toUpperCase() === data.toReveal?.header?.toUpperCase())
-          .forEach(header => {
-            header.highlight = true;
-          });
+      this.#headers.filter(header => header.name === data.toReveal?.header?.toLowerCase()).forEach(header => {
+        header.highlight = true;
+      });
     }
 
     this.#render();
diff --git a/test/unittests/front_end/panels/network/components/HeaderSectionRow_test.ts b/test/unittests/front_end/panels/network/components/HeaderSectionRow_test.ts
index ca3e6c5..1f4ac93 100644
--- a/test/unittests/front_end/panels/network/components/HeaderSectionRow_test.ts
+++ b/test/unittests/front_end/panels/network/components/HeaderSectionRow_test.ts
@@ -114,7 +114,7 @@
 
   it('displays info about blocked "Set-Cookie"-headers', async () => {
     const headerData: NetworkComponents.HeaderSectionRow.HeaderDescriptor = {
-      name: 'Set-Cookie',
+      name: 'set-cookie',
       value: 'secure=only; Secure',
       headerNotSet: false,
       setCookieBlockedReasons:
@@ -125,7 +125,7 @@
 
     const headerName = component.shadowRoot.querySelector('.header-name');
     assertElement(headerName, HTMLDivElement);
-    assert.strictEqual(headerName.textContent?.trim(), 'Set-Cookie:');
+    assert.strictEqual(headerName.textContent?.trim(), 'set-cookie:');
 
     const headerValue = component.shadowRoot.querySelector('.header-value');
     assertElement(headerValue, HTMLDivElement);
diff --git a/test/unittests/front_end/panels/network/components/RequestHeaderSection_test.ts b/test/unittests/front_end/panels/network/components/RequestHeaderSection_test.ts
index 2299f02..2679cde 100644
--- a/test/unittests/front_end/panels/network/components/RequestHeaderSection_test.ts
+++ b/test/unittests/front_end/panels/network/components/RequestHeaderSection_test.ts
@@ -76,7 +76,7 @@
     });
     assert.deepStrictEqual(sorted, [
       ['aa:', 'first'],
-      ['Ab:', 'second'],
+      ['ab:', 'second'],
       ['abc:', 'third'],
       ['name:', 'fourth'],
       ['test:', 'fifth'],
diff --git a/test/unittests/front_end/panels/network/components/RequestHeadersView_test.ts b/test/unittests/front_end/panels/network/components/RequestHeadersView_test.ts
index 1276364..cb88292 100644
--- a/test/unittests/front_end/panels/network/components/RequestHeadersView_test.ts
+++ b/test/unittests/front_end/panels/network/components/RequestHeadersView_test.ts
@@ -265,11 +265,11 @@
 
     const spy = sinon.spy(component, 'data', ['set']);
     assert.isTrue(spy.set.notCalled);
-    assert.deepStrictEqual(getRowsTextFromCategory(responseHeadersCategory), [['originalName:', 'originalValue']]);
+    assert.deepStrictEqual(getRowsTextFromCategory(responseHeadersCategory), [['originalname:', 'originalValue']]);
 
     request.responseHeaders = [{name: 'updatedName', value: 'updatedValue'}];
     assert.isTrue(spy.set.calledOnce);
-    assert.deepStrictEqual(getRowsTextFromCategory(responseHeadersCategory), [['updatedName:', 'updatedValue']]);
+    assert.deepStrictEqual(getRowsTextFromCategory(responseHeadersCategory), [['updatedname:', 'updatedValue']]);
 
     view.detach();
   });
@@ -299,7 +299,7 @@
     assertElement(responseHeadersCategory, HTMLElement);
     assert.deepStrictEqual(
         getRowsTextFromCategory(responseHeadersCategory),
-        [['DevTools:', 'rock'], ['foo:', 'bar'], ['highlightMe:', 'some value']]);
+        [['devtools:', 'rock'], ['foo:', 'bar'], ['highlightme:', 'some value']]);
 
     assert.deepStrictEqual(getRowHighlightStatus(responseHeadersCategory), [false, false, false]);
     view.revealHeader(NetworkForward.UIRequestLocation.UIHeaderSection.Response, 'HiGhLiGhTmE');
@@ -333,7 +333,7 @@
     assertElement(requestHeadersCategory, HTMLElement);
     assert.deepStrictEqual(
         getRowsTextFromCategory(requestHeadersCategory),
-        [['DevTools:', 'rock'], ['foo:', 'bar'], ['highlightMe:', 'some value']]);
+        [['devtools:', 'rock'], ['foo:', 'bar'], ['highlightme:', 'some value']]);
 
     assert.deepStrictEqual(getRowHighlightStatus(requestHeadersCategory), [false, false, false]);
     view.revealHeader(NetworkForward.UIRequestLocation.UIHeaderSection.Request, 'HiGhLiGhTmE');
diff --git a/test/unittests/front_end/panels/network/components/ResponseHeaderSection_test.ts b/test/unittests/front_end/panels/network/components/ResponseHeaderSection_test.ts
index f879ee0..7e67fe8 100644
--- a/test/unittests/front_end/panels/network/components/ResponseHeaderSection_test.ts
+++ b/test/unittests/front_end/panels/network/components/ResponseHeaderSection_test.ts
@@ -79,7 +79,7 @@
     assertElement(row, HTMLElement);
     assertShadowRoot(row.shadowRoot);
 
-    assert.strictEqual(row.shadowRoot.querySelector('.header-name')?.textContent?.trim(), 'Set-Cookie:');
+    assert.strictEqual(row.shadowRoot.querySelector('.header-name')?.textContent?.trim(), 'set-cookie:');
     assert.strictEqual(row.shadowRoot.querySelector('.header-value')?.textContent?.trim(), 'secure=only; Secure');
 
     const icon = row.shadowRoot.querySelector('devtools-icon');