Implement Critical-CH

Implement the Critical-CH response header as a URL Loader throttle. This
restarts/internally redirects the request when it sees a Critical-CH
header is can honor, as defined in the Client Hint Reliability
explainer.

https://github.com/WICG/client-hints-infrastructure/blob/master/reliability.md#critical-ch

Bug: 1127313
Change-Id: I3987d8d385f078ecb781a5895ab637522c8c8261
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2405697
Commit-Queue: Aaron Tagliaboschi <aarontag@chromium.org>
Reviewed-by: Yoav Weiss <yoavweiss@chromium.org>
Reviewed-by: Peter Conn <peconn@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Maksim Orlovich <morlovich@chromium.org>
Reviewed-by: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818945}
diff --git a/client-hints/critical-ch/iframe.https.window.js b/client-hints/critical-ch/iframe.https.window.js
new file mode 100644
index 0000000..87c68a3
--- /dev/null
+++ b/client-hints/critical-ch/iframe.https.window.js
@@ -0,0 +1,9 @@
+async_test((t) => {
+  var iframe = document.createElement("iframe");
+  iframe.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(iframe.contentDocument.body.textContent, "FAIL");});
+    t.done();
+  });
+  iframe.src = "resources/echo-critical-hint.py";
+  document.body.appendChild(iframe);
+}, "Critical-CH iframe");
diff --git a/client-hints/critical-ch/mis-matched-count.https.window.js b/client-hints/critical-ch/mis-matched-count.https.window.js
new file mode 100644
index 0000000..b0c8d87
--- /dev/null
+++ b/client-hints/critical-ch/mis-matched-count.https.window.js
@@ -0,0 +1,10 @@
+// META: script=/common/utils.js
+
+async_test((t) => {
+  var popup_window = window.open("resources/echo-critical-hint.py?mismatch=true&token="+token());
+  assert_not_equals(popup_window, null, "Popup windows not allowed?");
+  popup_window.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(popup_window.document.body.textContent, "1")});
+    t.done();
+  });
+}, "Critical-CH navigation")
diff --git a/client-hints/critical-ch/mis-matched.https.window.js b/client-hints/critical-ch/mis-matched.https.window.js
new file mode 100644
index 0000000..fcfd541
--- /dev/null
+++ b/client-hints/critical-ch/mis-matched.https.window.js
@@ -0,0 +1,8 @@
+async_test((t) => {
+  var popup_window = window.open("resources/echo-critical-hint.py?mismatch=true");
+  assert_not_equals(popup_window, null, "Popup windows not allowed?");
+  popup_window.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(popup_window.document.body.textContent, "FAIL")});
+    t.done();
+  });
+}, "Critical-CH navigation")
diff --git a/client-hints/critical-ch/navigation.https.window.js b/client-hints/critical-ch/navigation.https.window.js
new file mode 100644
index 0000000..831987e
--- /dev/null
+++ b/client-hints/critical-ch/navigation.https.window.js
@@ -0,0 +1,8 @@
+async_test((t) => {
+  var popup_window = window.open("resources/echo-critical-hint.py");
+  assert_not_equals(popup_window, null, "Popup windows not allowed?");
+  popup_window.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(popup_window.document.body.textContent, "PASS")});
+    t.done();
+  });
+}, "Critical-CH navigation")
diff --git a/client-hints/critical-ch/non-secure.http.window.js b/client-hints/critical-ch/non-secure.http.window.js
new file mode 100644
index 0000000..46c00ff
--- /dev/null
+++ b/client-hints/critical-ch/non-secure.http.window.js
@@ -0,0 +1,8 @@
+async_test((t) => {
+  var popup_window = window.open("resources/echo-critical-hint.py");
+  assert_not_equals(popup_window, null, "Popup windows not allowed?");
+  popup_window.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(popup_window.document.body.textContent, "FAIL")});
+    t.done();
+  });
+}, "Critical-CH non-secure navigation")
diff --git a/client-hints/critical-ch/request-count.https.window.js b/client-hints/critical-ch/request-count.https.window.js
new file mode 100644
index 0000000..36c0ef8
--- /dev/null
+++ b/client-hints/critical-ch/request-count.https.window.js
@@ -0,0 +1,10 @@
+// META: script=/common/utils.js
+
+async_test((t) => {
+  var popup_window = window.open("resources/echo-critical-hint.py?token="+token());
+  assert_not_equals(popup_window, null, "Popup windows not allowed?");
+  popup_window.addEventListener('load', (e) => {
+    t.step(()=>{assert_equals(popup_window.document.body.textContent, "2")});
+    t.done();
+  });
+}, "Critical-CH navigation")
diff --git a/client-hints/critical-ch/resources/echo-critical-hint.py b/client-hints/critical-ch/resources/echo-critical-hint.py
new file mode 100644
index 0000000..4e2577a
--- /dev/null
+++ b/client-hints/critical-ch/resources/echo-critical-hint.py
@@ -0,0 +1,43 @@
+import sys
+
+def main(request, response):
+    """
+    Simple handler that sets a response header based on which client hint
+    request headers were received.
+    """
+
+    response.headers.append(b"Content-Type", b"text/html; charset=UTF-8")
+    response.headers.append(b"Access-Control-Allow-Origin", b"*")
+    response.headers.append(b"Access-Control-Allow-Headers", b"*")
+    response.headers.append(b"Access-Control-Expose-Headers", b"*")
+
+    response.headers.append(b"Accept-CH", b"device-memory")
+
+    critical = b"device-memory"
+    if(request.GET.first(b"mismatch", None) is not None):
+      critical = b"viewport-width"
+
+    response.headers.append(b"Critical-CH", critical)
+
+    response.headers.append(b"Cache-Control", b"no-store")
+
+    result = "FAIL"
+
+    if b"device-memory" in request.headers:
+      result = "PASS"
+
+    token = request.GET.first(b"token", None)
+    if(token is not None):
+      with request.server.stash.lock:
+        count = request.server.stash.take(token)
+        if(count == None):
+          count = 1
+        else:
+          count += 1
+        request.server.stash.put(token, count)
+        result = str(count)
+
+    if b"viewport-width" in request.headers:
+      result = "MISMATCH"
+
+    response.content = result
diff --git a/client-hints/critical-ch/subresource.https.window.js b/client-hints/critical-ch/subresource.https.window.js
new file mode 100644
index 0000000..4949335
--- /dev/null
+++ b/client-hints/critical-ch/subresource.https.window.js
@@ -0,0 +1,7 @@
+promise_test(() =>
+  fetch("resources/echo-critical-hint.py")
+      .then((r) => r.text())
+      .then((r) => {
+        assert_equals(r, "FAIL");
+      })
+, "Critical-CH");