Implement `Sec-Fetch-Mode`

This patch implements `Sec-Fetch-Mode`, which adds the current CORS
mode to outgoing, secure requests, as defined in
https://mikewest.github.io/sec-metadata/#sec-fetch-mode-header.

Bug: 843478
Change-Id: I811bfa86bdac1600b8abdd275d9526f6408e62e2
Reviewed-on: https://chromium-review.googlesource.com/c/1466362
Reviewed-by: Camille Lamy <clamy@chromium.org>
Commit-Queue: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#631651}
diff --git a/fetch/sec-metadata/embed.tentative.https.sub.html b/fetch/sec-metadata/embed.tentative.https.sub.html
index 1c69c02..6f0c439 100644
--- a/fetch/sec-metadata/embed.tentative.https.sub.html
+++ b/fetch/sec-metadata/embed.tentative.https.sub.html
@@ -13,7 +13,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"embed", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"embed", "site":"same-origin", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -32,7 +32,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"embed", "site":"same-site", "user":"?F"};
+        let expected = {"dest":"embed", "site":"same-site", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -51,7 +51,7 @@
       let e = document.createElement('embed');
       e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"embed", "site":"cross-site", "user":"?F"};
+        let expected = {"dest":"embed", "site":"cross-site", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/fetch/sec-metadata/fetch.tentative.https.sub.html b/fetch/sec-metadata/fetch.tentative.https.sub.html
index f6460b4..dc4b977 100644
--- a/fetch/sec-metadata/fetch.tentative.https.sub.html
+++ b/fetch/sec-metadata/fetch.tentative.https.sub.html
@@ -3,6 +3,7 @@
 <script src=/resources/testharnessreport.js></script>
 <script src=/fetch/sec-metadata/resources/helper.js></script>
 <script>
+  // Site
   promise_test(t => {
     return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py")
         .then(r => r.json())
@@ -10,7 +11,8 @@
           assert_header_equals(j, {
             "dest": "empty",
             "site": "same-origin",
-            "user":"?F"
+            "user": "?F",
+            "mode": "cors",
           });
         });
   }, "Same-origin fetch");
@@ -22,7 +24,8 @@
           assert_header_equals(j, {
             "dest": "empty",
             "site": "same-site",
-            "user":"?F"
+            "user": "?F",
+            "mode": "cors",
           });
         });
   }, "Same-site fetch");
@@ -34,8 +37,49 @@
           assert_header_equals(j, {
             "dest": "empty",
             "site": "cross-site",
-            "user":"?F"
+            "user": "?F",
+            "mode": "cors",
           });
         });
   }, "Cross-site fetch");
+
+  // Mode
+  promise_test(t => {
+    return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "same-origin"})
+        .then(r => r.json())
+        .then(j => {
+          assert_header_equals(j, {
+            "dest": "empty",
+            "site": "same-origin",
+            "user": "?F",
+            "mode": "same-origin",
+          });
+        });
+  }, "Same-origin mode");
+
+  promise_test(t => {
+    return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "cors"})
+        .then(r => r.json())
+        .then(j => {
+          assert_header_equals(j, {
+            "dest": "empty",
+            "site": "same-origin",
+            "user": "?F",
+            "mode": "cors",
+          });
+        });
+  }, "CORS mode");
+
+  promise_test(t => {
+    return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "no-cors"})
+        .then(r => r.json())
+        .then(j => {
+          assert_header_equals(j, {
+            "dest": "empty",
+            "site": "same-origin",
+            "user": "?F",
+            "mode": "no-cors",
+          });
+        });
+  }, "no-CORS mode");
 </script>
diff --git a/fetch/sec-metadata/font.tentative.https.sub.html b/fetch/sec-metadata/font.tentative.https.sub.html
index d2bcf69..9792f2d 100644
--- a/fetch/sec-metadata/font.tentative.https.sub.html
+++ b/fetch/sec-metadata/font.tentative.https.sub.html
@@ -46,7 +46,7 @@
     promise_test(t => {
       return new Promise((resolve, reject) => {
         let key = "font-same-origin";
-        let expected = {"dest":"font", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"font", "site":"same-origin", "user":"?F", "mode": "cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -58,7 +58,7 @@
     promise_test(t => {
       return new Promise((resolve, reject) => {
         let key = "font-same-site";
-        let expected = {"dest":"font", "site":"same-site", "user":"?F"};
+        let expected = {"dest":"font", "site":"same-site", "user":"?F", "mode": "cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -70,7 +70,7 @@
     promise_test(t => {
       return new Promise((resolve, reject) => {
         let key = "font-cross-site";
-        let expected = {"dest":"font", "site":"cross-site", "user":"?F"};
+        let expected = {"dest":"font", "site":"cross-site", "user":"?F", "mode": "cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/fetch/sec-metadata/iframe.tentative.https.sub.html b/fetch/sec-metadata/iframe.tentative.https.sub.html
index 6ee1430..73bd907 100644
--- a/fetch/sec-metadata/iframe.tentative.https.sub.html
+++ b/fetch/sec-metadata/iframe.tentative.https.sub.html
@@ -14,7 +14,8 @@
       assert_header_equals(e.data, {
         "dest": "nested-document",
         "site": "same-origin",
-        "user":"?F"
+        "user": "?F",
+        "mode": "navigate"
       });
       t.done();
     }));
@@ -32,7 +33,8 @@
       assert_header_equals(e.data, {
         "dest": "nested-document",
         "site": "same-site",
-        "user": "?F"
+        "user": "?F",
+        "mode": "navigate"
       });
       t.done();
     }));
@@ -50,7 +52,8 @@
       assert_header_equals(e.data, {
         "dest": "nested-document",
         "site": "cross-site",
-        "user": "?F"
+        "user": "?F",
+        "mode": "navigate"
       });
       t.done();
     }));
diff --git a/fetch/sec-metadata/iframe.tentative.sub.html b/fetch/sec-metadata/iframe.tentative.sub.html
index ce31a81..eab2d3f 100644
--- a/fetch/sec-metadata/iframe.tentative.sub.html
+++ b/fetch/sec-metadata/iframe.tentative.sub.html
@@ -14,7 +14,8 @@
       assert_header_equals(e.data, {
         "dest": "",
         "site": "",
-        "user": ""
+        "user": "",
+        "mode": "",
       });
       t.done();
     }));
@@ -32,7 +33,8 @@
       assert_header_equals(e.data, {
         "dest": "",
         "site": "",
-        "user": ""
+        "user": "",
+        "mode": "",
       });
       t.done();
     }));
@@ -50,7 +52,8 @@
       assert_header_equals(e.data, {
         "dest": "",
         "site": "",
-        "user": ""
+        "user": "",
+        "mode": "",
       });
       t.done();
     }));
diff --git a/fetch/sec-metadata/img.tentative.https.sub.html b/fetch/sec-metadata/img.tentative.https.sub.html
index c5c3895..252b220 100644
--- a/fetch/sec-metadata/img.tentative.https.sub.html
+++ b/fetch/sec-metadata/img.tentative.https.sub.html
@@ -21,7 +21,8 @@
         assert_header_equals(got, {
           "dest": "image",
           "site": "same-origin",
-          "user": "?F"
+          "user": "?F",
+          "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
       [],
@@ -42,7 +43,8 @@
         assert_header_equals(got, {
           "dest": "image",
           "site": "same-site",
-          "user": "?F"
+          "user": "?F",
+          "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
       [],
@@ -63,7 +65,8 @@
         assert_header_equals(got, {
           "dest": "image",
           "site": "cross-site",
-          "user": "?F"
+          "user": "?F",
+          "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
       [],
diff --git a/fetch/sec-metadata/object.tentative.https.sub.html b/fetch/sec-metadata/object.tentative.https.sub.html
index 0b98394..2a0e8de 100644
--- a/fetch/sec-metadata/object.tentative.https.sub.html
+++ b/fetch/sec-metadata/object.tentative.https.sub.html
@@ -13,7 +13,7 @@
       let e = document.createElement('object');
       e.data = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"object", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"object", "site":"same-origin", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -32,7 +32,7 @@
       let e = document.createElement('object');
       e.data = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"object", "site":"same-site", "user":"?F"};
+        let expected = {"dest":"object", "site":"same-site", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -51,7 +51,7 @@
       let e = document.createElement('object');
       e.data = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"object", "site":"cross-site", "user":"?F"};
+        let expected = {"dest":"object", "site":"cross-site", "user":"?F", "mode":"no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/fetch/sec-metadata/redirect/cross-site-redirect.tentative.https.sub.html b/fetch/sec-metadata/redirect/cross-site-redirect.tentative.https.sub.html
index 56d88d9..f88cf14 100644
--- a/fetch/sec-metadata/redirect/cross-site-redirect.tentative.https.sub.html
+++ b/fetch/sec-metadata/redirect/cross-site-redirect.tentative.https.sub.html
@@ -12,7 +12,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
@@ -38,7 +38,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
@@ -64,7 +64,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
diff --git a/fetch/sec-metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html b/fetch/sec-metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html
index f6d18f5..688c697 100644
--- a/fetch/sec-metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html
+++ b/fetch/sec-metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html
@@ -14,7 +14,7 @@
       e.src = "https://{{host}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-origin
       "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// cross-site
       "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;// same-origin
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
diff --git a/fetch/sec-metadata/redirect/multiple-redirect-same-site.tentative.https.sub.html b/fetch/sec-metadata/redirect/multiple-redirect-same-site.tentative.https.sub.html
index 4756a79..bc79f78 100644
--- a/fetch/sec-metadata/redirect/multiple-redirect-same-site.tentative.https.sub.html
+++ b/fetch/sec-metadata/redirect/multiple-redirect-same-site.tentative.https.sub.html
@@ -14,7 +14,7 @@
       e.src = "https://{{host}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-origin
       "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-site
       "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;// same-origin
-      let expected = {"dest":"image", "site":"same-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
diff --git a/fetch/sec-metadata/redirect/same-origin-redirect.tentative.https.sub.html b/fetch/sec-metadata/redirect/same-origin-redirect.tentative.https.sub.html
index 8558d68..a532392 100644
--- a/fetch/sec-metadata/redirect/same-origin-redirect.tentative.https.sub.html
+++ b/fetch/sec-metadata/redirect/same-origin-redirect.tentative.https.sub.html
@@ -12,7 +12,7 @@
 
       let e = document.createElement('img');
       e.src = "/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"same-origin", "user":"?F"};
+      let expected = {"dest":"image", "site":"same-origin", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@@ -39,7 +39,7 @@
 
       let e = document.createElement('img');
       e.src = "/xhr/resources/redirect.py?location=https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"same-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@@ -66,7 +66,7 @@
 
       let e = document.createElement('img');
       e.src = "/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
diff --git a/fetch/sec-metadata/redirect/same-site-redirect.tentative.https.sub.html b/fetch/sec-metadata/redirect/same-site-redirect.tentative.https.sub.html
index 8e05b25..92749ae 100644
--- a/fetch/sec-metadata/redirect/same-site-redirect.tentative.https.sub.html
+++ b/fetch/sec-metadata/redirect/same-site-redirect.tentative.https.sub.html
@@ -12,7 +12,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"same-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@@ -39,7 +39,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"same-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@@ -66,7 +66,7 @@
 
       let e = document.createElement('img');
       e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
-      let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
 
       e.onload = e => {
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
diff --git a/fetch/sec-metadata/report.tentative.https.sub.html b/fetch/sec-metadata/report.tentative.https.sub.html
index 1cfa864..f1d8e06 100644
--- a/fetch/sec-metadata/report.tentative.https.sub.html
+++ b/fetch/sec-metadata/report.tentative.https.sub.html
@@ -22,9 +22,9 @@
     document.addEventListener("securitypolicyviolation", (e) => {
       counter++;
       if (counter == 3) {
-        generate_test({"dest":"report", "site":"same-origin", "user":"?F"}, "same-origin");
-        generate_test({"dest":"report", "site":"same-site", "user":"?F"}, "same-site");
-        generate_test({"dest":"report", "site":"cross-site", "user":"?F"}, "cross-site");
+        generate_test({"dest":"report", "site":"same-origin", "user":"?F", "mode": "no-cors"}, "same-origin");
+        generate_test({"dest":"report", "site":"same-site", "user":"?F", "mode": "no-cors"}, "same-site");
+        generate_test({"dest":"report", "site":"cross-site", "user":"?F", "mode": "no-cors"}, "cross-site");
       }
     });
   }, "Initialization.");
diff --git a/fetch/sec-metadata/resources/helper.js b/fetch/sec-metadata/resources/helper.js
index 1a9ec15..4bee689 100644
--- a/fetch/sec-metadata/resources/helper.js
+++ b/fetch/sec-metadata/resources/helper.js
@@ -4,8 +4,7 @@
     value = JSON.parse(value);
   }
   assert_equals(value.dest, expected.dest, "dest");
-  // Mode is commented out as no test cases have been filled out yet
-  // assert_equals(value.mode, expected.mode, "mode");
+  assert_equals(value.mode, expected.mode, "mode");
   assert_equals(value.site, expected.site, "site");
   assert_equals(value.user, expected.user, "user");
 }
diff --git a/fetch/sec-metadata/script.tentative.https.sub.html b/fetch/sec-metadata/script.tentative.https.sub.html
index d76378f..a35e753 100644
--- a/fetch/sec-metadata/script.tentative.https.sub.html
+++ b/fetch/sec-metadata/script.tentative.https.sub.html
@@ -12,7 +12,8 @@
     assert_header_equals(header, {
       "dest": "script",
       "site": "same-origin",
-      "user":"?F"
+      "user": "?F",
+      "mode": "no-cors",
     });
   }, "Same-origin script");
 </script>
@@ -26,7 +27,8 @@
     assert_header_equals(header, {
       "dest": "script",
       "site": "same-site",
-      "user":"?F"
+      "user": "?F",
+      "mode": "no-cors",
     });
   }, "Same-site script");
 </script>
@@ -40,7 +42,23 @@
     assert_header_equals(header, {
       "dest": "script",
       "site": "cross-site",
-      "user":"?F"
+      "user": "?F",
+      "mode": "no-cors",
     });
   }, "Cross-site script");
 </script>
+
+<!-- Same-origin script, CORS mode -->
+<script src="https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-script.py" crossorigin="anonymous"></script>
+<script>
+  test(t => {
+    t.add_cleanup(_ => { header = null; });
+
+    assert_header_equals(header, {
+      "dest": "script",
+      "site": "same-origin",
+      "user": "?F",
+      "mode": "cors",
+    });
+  }, "Same-origin CORS script");
+</script>
diff --git a/fetch/sec-metadata/script.tentative.sub.html b/fetch/sec-metadata/script.tentative.sub.html
index 482f4b9..3218a40 100644
--- a/fetch/sec-metadata/script.tentative.sub.html
+++ b/fetch/sec-metadata/script.tentative.sub.html
@@ -12,7 +12,8 @@
     assert_header_equals(header, {
       "dest": "",
       "site": "",
-      "user": ""
+      "user": "",
+      "mode": "",
     });
   }, "Non-secure same-origin script => No headers");
 </script>
@@ -26,7 +27,8 @@
     assert_header_equals(header, {
       "dest": "",
       "site": "",
-      "user": ""
+      "user": "",
+      "mode": "",
     });
   }, "Non-secure same-site script => No headers");
 </script>
@@ -40,7 +42,8 @@
     assert_header_equals(header, {
       "dest": "",
       "site": "",
-      "user": ""
+      "user": "",
+      "mode": "",
     });
   }, "Non-secure cross-site script => No headers");
 </script>
diff --git a/fetch/sec-metadata/serviceworker.tentative.https.sub.html b/fetch/sec-metadata/serviceworker.tentative.https.sub.html
index d0b86d2..5b7ee77 100644
--- a/fetch/sec-metadata/serviceworker.tentative.https.sub.html
+++ b/fetch/sec-metadata/serviceworker.tentative.https.sub.html
@@ -35,7 +35,7 @@
     promise_test(t => {
     return new Promise((resolve, reject) => {
       let key = "serviceworker-same-origin";
-      let expected = {"dest":"serviceworker", "site":"same-origin", "user":"?F"};
+      let expected = {"dest":"serviceworker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
       fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
         .then(response => response.text())
         .then(text => assert_header_equals(text, expected))
diff --git a/fetch/sec-metadata/sharedworker.tentative.https.sub.html b/fetch/sec-metadata/sharedworker.tentative.https.sub.html
index 66f7d5b..cfeadd8 100644
--- a/fetch/sec-metadata/sharedworker.tentative.https.sub.html
+++ b/fetch/sec-metadata/sharedworker.tentative.https.sub.html
@@ -26,7 +26,7 @@
     promise_test(t => {
       return new Promise((resolve, reject) => {
         let key = "sharedworker-same-origin";
-        let expected = {"dest":"sharedworker", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"sharedworker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
 
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
diff --git a/fetch/sec-metadata/style.tentative.https.sub.html b/fetch/sec-metadata/style.tentative.https.sub.html
index 9697db7..4ae1266 100644
--- a/fetch/sec-metadata/style.tentative.https.sub.html
+++ b/fetch/sec-metadata/style.tentative.https.sub.html
@@ -14,7 +14,7 @@
       e.rel = "stylesheet";
       e.href = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"style", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"style", "site":"same-origin", "user":"?F", "mode": "no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -34,7 +34,7 @@
       e.rel = "stylesheet";
       e.href = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"style", "site":"same-site", "user":"?F"};
+        let expected = {"dest":"style", "site":"same-site", "user":"?F", "mode": "no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -54,7 +54,7 @@
       e.rel = "stylesheet";
       e.href = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
       e.onload = e => {
-        let expected = {"dest":"style", "site":"cross-site", "user":"?F"};
+        let expected = {"dest":"style", "site":"cross-site", "user":"?F", "mode": "no-cors"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
@@ -65,6 +65,27 @@
       document.body.appendChild(e);
     })
   }, "Cross-Site style");
+
+  promise_test(t => {
+    return new Promise((resolve, reject) => {
+      let key = "style-same-origin";
+
+      let e = document.createElement('link');
+      e.rel = "stylesheet";
+      e.href = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
+      e.crossOrigin = "anonymous";
+      e.onload = e => {
+        let expected = {"dest":"style", "site":"same-origin", "user":"?F", "mode": "cors"};
+        fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
+          .then(response => response.text())
+          .then(text => assert_header_equals(text, expected))
+          .then(_ => resolve())
+          .catch(e => reject(e));
+      };
+
+      document.body.appendChild(e);
+    })
+  }, "Same-Origin, cors style");
 </script>
 </html>
 
diff --git a/fetch/sec-metadata/track.tentative.https.sub.html b/fetch/sec-metadata/track.tentative.https.sub.html
index b9dfabf..89933f2 100644
--- a/fetch/sec-metadata/track.tentative.https.sub.html
+++ b/fetch/sec-metadata/track.tentative.https.sub.html
@@ -29,7 +29,12 @@
       let el = createTrack();
       el.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin";
       el.onload = t.step_func(_ => {
-        expected = {"dest":"track", "site":"same-origin", "user":"?F"};
+        expected = {
+          "dest": "track",
+          "site": "same-origin",
+          "user": "?F",
+          "mode": "cors" // Because the `video` element has `crossorigin`
+        };
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-origin")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected))
@@ -46,7 +51,12 @@
       let el = createTrack();
       el.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-site";
       el.onload = t.step_func(_ => {
-        expected = {"dest":"track", "site":"same-site", "user":"?F"};
+        expected = {
+          "dest": "track",
+          "site": "same-site",
+          "user": "?F",
+          "mode": "cors" // Because the `video` element has `crossorigin`
+        };
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-site")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected))
@@ -65,7 +75,12 @@
       let el = createTrack();
       el.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-cross-site";
       el.onload = t.step_func(_ => {
-        expected = {"dest":"track", "site":"cross-site", "user":"?F"};
+        expected = {
+          "dest": "track",
+          "site": "cross-site",
+          "user": "?F",
+          "mode": "cors" // Because the `video` element has `crossorigin`
+        };
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-cross-site")
             .then(response => response.text())
             .then(text => assert_header_equals(text, expected))
@@ -76,4 +91,30 @@
       document.body.appendChild(video);
     });
   }, "Cross-Site track");
+
+  promise_test(t => {
+    return new Promise((resolve, reject) => {
+      let video = createVideoElement();
+
+      // Unset `crossorigin` to change the CORS mode:
+      video.crossOrigin = undefined;
+
+      let el = createTrack();
+      el.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin";
+      el.onload = t.step_func(_ => {
+        expected = {
+          "dest":"track",
+          "site":"same-origin",
+          "user":"?F",
+          "mode": "same-origin"
+        };
+        fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-origin")
+            .then(response => response.text())
+            .then(text => assert_header_equals(text, expected))
+            .then(_ => resolve());
+      });
+      video.appendChild(el);
+      document.body.appendChild(video);
+    });
+  }, "Same-Origin, CORS track");
 </script>
diff --git a/fetch/sec-metadata/window-open.tentative.https.sub.html b/fetch/sec-metadata/window-open.tentative.https.sub.html
index 236268e..2957dad 100644
--- a/fetch/sec-metadata/window-open.tentative.https.sub.html
+++ b/fetch/sec-metadata/window-open.tentative.https.sub.html
@@ -17,7 +17,8 @@
       assert_header_equals(e.data, {
         "dest": "document",
         "site": "same-origin",
-        "user":"?F"
+        "user": "?F",
+        "mode": "navigate",
       });
       t.done();
     }));
@@ -33,7 +34,8 @@
       assert_header_equals(e.data, {
         "dest": "document",
         "site": "same-site",
-        "user":"?F"
+        "user": "?F",
+        "mode": "navigate",
       });
       t.done();
     }));
@@ -49,7 +51,8 @@
       assert_header_equals(e.data, {
         "dest": "document",
         "site": "cross-site",
-        "user":"?F"
+        "user": "?F",
+        "mode": "navigate",
       });
       t.done();
     }));
@@ -68,7 +71,8 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "same-origin",
-          "user": "?T"
+          "user": "?T",
+          "mode": "navigate",
         });
         t.done();
       }));
@@ -89,7 +93,8 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "same-site",
-          "user": "?T"
+          "user": "?T",
+          "mode": "navigate",
         });
         t.done();
       }));
@@ -110,7 +115,8 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "cross-site",
-          "user": "?T"
+          "user": "?T",
+          "mode": "navigate",
         });
         t.done();
       }));
diff --git a/fetch/sec-metadata/worker.tentative.https.sub.html b/fetch/sec-metadata/worker.tentative.https.sub.html
index fcffe91..89be6f0 100644
--- a/fetch/sec-metadata/worker.tentative.https.sub.html
+++ b/fetch/sec-metadata/worker.tentative.https.sub.html
@@ -10,7 +10,7 @@
       let key = "worker-same-origin";
       let w = new Worker("/fetch/sec-metadata/resources/record-header.py?file=" + key);
       w.onmessage = e => {
-        let expected = {"dest":"worker", "site":"same-origin", "user":"?F"};
+        let expected = {"dest":"worker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
         fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected))
diff --git a/fetch/sec-metadata/xslt.tentative.https.sub.html b/fetch/sec-metadata/xslt.tentative.https.sub.html
index 32349c9..eea2329 100644
--- a/fetch/sec-metadata/xslt.tentative.https.sub.html
+++ b/fetch/sec-metadata/xslt.tentative.https.sub.html
@@ -12,21 +12,21 @@
       return;
 
     promise_test(t => {
-      let expected = {"dest":"xslt", "site":"same-origin", "user":"?F"};
+      let expected = {"dest":"xslt", "site":"same-origin", "user":"?F", "mode": "same-origin"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-origin")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));
     }, "Same-Origin xslt");
 
     promise_test(t => {
-      let expected = {"dest":"xslt", "site":"same-site", "user":"?F"};
+      let expected = {"dest":"xslt", "site":"same-site", "user":"?F", "mode": "no-cors"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-site")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));
     }, "Same-site xslt");
 
     promise_test(t => {
-      let expected = {"dest":"xslt", "site":"cross-site", "user":"?F"};
+      let expected = {"dest":"xslt", "site":"cross-site", "user":"?F", "mode": "no-cors"};
       return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-cross-site")
           .then(response => response.text())
           .then(text => assert_header_equals(text, expected));