Support enum-type parameterized feature policy

Note: this CL does not include the parser implementation

This allows us to specify value as following:

feature_name *(enum1) 'self'(enum2);

To specify a enum, you need to provide max value and min value,
otherwise 0 and numeric_limit<unsigned>::max() is used.

To check if a feature is enabled, instead of using the threshold
value, call feature_policy->GetFeatureValueForOrigin(feature, origin)
and interpret the enum to get the feature state.

Bug: 924568
Change-Id: I2e5ca7e82cfbd3b9258dd84fc6735e9b548099f4
diff --git a/feature-policy/parameters/feature-parameters-inf.html b/feature-policy/parameters/feature-parameters-inf.html
new file mode 100644
index 0000000..db21427
--- /dev/null
+++ b/feature-policy/parameters/feature-parameters-inf.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test oversized-images policy with threshold 'inf'</title>
+</head>
+<body>
+  <!-- The sample image has an intrinsic image size of 200x200px -->
+  <img src="resources/sample-1.png" width="200" height="200">
+  <img src="resources/sample-1.png" width="100" height="200">
+  <img src="resources/sample-1.png" width="50" height="200">
+  <br>
+  <img src="resources/sample-1.png" width="200" height="100">
+  <img src="resources/sample-1.png" width="100" height="100">
+  <img src="resources/sample-1.png" width="50" height="100">
+  <br>
+  <img src="resources/sample-1.png" width="200" height="50">
+  <img src="resources/sample-1.png" width="100" height="50">
+  <img src="resources/sample-1.png" width="50" height="50">
+</body>
+</html>
diff --git a/feature-policy/parameters/feature-parameters-inf.html.headers b/feature-policy/parameters/feature-parameters-inf.html.headers
new file mode 100644
index 0000000..1ec0034
--- /dev/null
+++ b/feature-policy/parameters/feature-parameters-inf.html.headers
@@ -0,0 +1 @@
+Feature-Policy: oversized-images (inf)
diff --git a/feature-policy/parameters/feature-parameters-with-frames.html b/feature-policy/parameters/feature-parameters-with-frames.html
new file mode 100644
index 0000000..de0a3ab
--- /dev/null
+++ b/feature-policy/parameters/feature-parameters-with-frames.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/feature-policy/resources/featurepolicy.js"></script>
+  <title>Test oversized-images policy with threshold 1.5</title>
+</head>
+<body>
+  <iframe scrolling="no" name="a" style="overflow:hidden" width="380" height="220"></iframe>
+  <iframe scrolling="no" name="b" style="overflow:hidden" width="380" height="220"></iframe>
+  <iframe scrolling="no" name="c" style="overflow:hidden" width="380" height="220"></iframe>
+  <iframe scrolling="no" name="d" style="overflow:hidden" width="380" height="220"></iframe>
+  <iframe scrolling="no" name="e" style="overflow:hidden" width="380" height="220"></iframe>
+
+  <script>
+    const frame_to_test_map = {};
+    window.addEventListener('message', ev => {
+      if (ev.data.type == "finished") {
+        if (frame_to_test_map.hasOwnProperty(ev.data.name)) {
+          frame_to_test_map[ev.data.name].done();
+        }
+      }
+    });
+    const config = {
+      a: {threshold: 0.0, blocked: 3},
+      b: {threshold: 1.0, blocked: 2},
+      c: {threshold: 2.5, blocked: 1},
+      d: {threshold: 4.0, blocked: 0},
+      e: {threshold: "inf", blocked: 0}
+    };
+    const iframes = document.querySelectorAll('iframe');
+    const total_iframes = iframes.length;
+    iframes.forEach(iframe => {
+      const frame_config = config[iframe.name]
+      async_test(t => {
+        frame_to_test_map[iframe.name] = t;
+        iframe.src = "resources/feature-parameters-frame.html?name="+iframe.name+"&n="+frame_config.blocked+"&pipe=header(Feature-Policy,oversized-images%20("+frame_config.threshold+"\\);)";
+      }, "Test frame with threshold " + frame_config.threshold + " should block " + frame_config.blocked + " images.");
+    });
+  </script>
+</body>
+</html>
diff --git a/feature-policy/parameters/feature-parameters.html b/feature-policy/parameters/feature-parameters.html
new file mode 100644
index 0000000..9830f93
--- /dev/null
+++ b/feature-policy/parameters/feature-parameters.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/feature-policy/resources/featurepolicy.js"></script>
+  <title>Test oversized-images policy with threshold 1.5</title>
+</head>
+<body>
+  <!-- The sample image has an intrinsic image size of 200x200px -->
+  <img src="resources/sample-1.png" width="200" height="200">
+  <img src="resources/sample-1.png" width="100" height="200">
+  <img src="resources/sample-1.png" width="50" height="200">
+  <br>
+  <img src="resources/sample-1.png" width="200" height="100">
+  <img src="resources/sample-1.png" width="100" height="100">
+  <img src="resources/sample-1.png" width="50" height="100">
+  <br>
+  <img src="resources/sample-1.png" width="200" height="50">
+  <img src="resources/sample-1.png" width="100" height="50">
+  <img src="resources/sample-1.png" width="50" height="50">
+
+  <script>
+    expect_reports(8, "oversized-images", "8 images should be blocked by policy");
+  </script>
+</body>
+</html>
diff --git a/feature-policy/parameters/feature-parameters.html.headers b/feature-policy/parameters/feature-parameters.html.headers
new file mode 100644
index 0000000..b4fa805
--- /dev/null
+++ b/feature-policy/parameters/feature-parameters.html.headers
@@ -0,0 +1 @@
+Feature-Policy: oversized-images (1.5)
diff --git a/feature-policy/parameters/resources/feature-parameters-frame.html b/feature-policy/parameters/resources/feature-parameters-frame.html
new file mode 100644
index 0000000..4f01f85
--- /dev/null
+++ b/feature-policy/parameters/resources/feature-parameters-frame.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/feature-policy/resources/featurepolicy.js"></script>
+  <title>Test oversized-images policy in subframe</title>
+</head>
+<body>
+  <!-- The sample image has an intrinsic image size of 200x200px -->
+  <img width="200" height="200">
+  <img width="100" height="200">
+  <img width="50" height="200">
+
+  <script>
+    const policy_name = "oversized-images";
+    const params = new URLSearchParams(document.location.search);
+    const frame_name = params.get('name');
+    const expected_report_count = +params.get('n');
+    var num_received_reports = 0;
+
+    const images = document.querySelectorAll('img');
+    const total_images = images.length;
+    var images_loaded = 0;
+
+    const notifyIfDone = () => {
+        if (num_received_reports >= expected_report_count &&
+            images_loaded == total_images) {
+            parent.postMessage({
+                "type": "finished",
+                "name": frame_name
+            },"*");
+        }
+    };
+
+    images.forEach(image => {
+        image.addEventListener('load', () => { images_loaded++; notifyIfDone(); });
+        image.src = "sample-1.png";
+    });
+
+    new ReportingObserver((reports, observer) => {
+        const relevant_reports = reports.filter(r => (r.body.featureId === policy_name));
+        num_received_reports += relevant_reports.length;
+        notifyIfDone();
+    }, {types: ['feature-policy-violation'], buffered: true}).observe();
+  </script>
+
+</body>
+</html>
diff --git a/feature-policy/parameters/resources/sample-1.png b/feature-policy/parameters/resources/sample-1.png
new file mode 100644
index 0000000..9290192
--- /dev/null
+++ b/feature-policy/parameters/resources/sample-1.png
Binary files differ
diff --git a/feature-policy/resources/featurepolicy.js b/feature-policy/resources/featurepolicy.js
index e2577f3..744c4c6 100644
--- a/feature-policy/resources/featurepolicy.js
+++ b/feature-policy/resources/featurepolicy.js
@@ -433,3 +433,16 @@
     assert_false(frame_policy.allowedFeatures().includes(feature));
   }
 }
+
+function expect_reports(report_count, policy_name, description) {
+  async_test(t => {
+    var num_received_reports = 0;
+    new ReportingObserver(t.step_func((reports, observer) => {
+        const relevant_reports = reports.filter(r => (r.body.featureId === policy_name));
+        num_received_reports += relevant_reports.length;
+        if (num_received_reports >= report_count) {
+            t.done();
+        }
+   }), {types: ['feature-policy-violation'], buffered: true}).observe();
+  }, description);
+}