Add vr feature policy

This CL enables vr feature policy. So that, if you want to disable vr within
your application, you can delivering the following HTTP response header:
Feature-Policy: vr 'none'
If you want to enable vr for all frames(including cross-origin iframes),
delivering this:
Feature-Policy: vr *
If you want to enable vr just for your own origin, delivering this:
Feature-Policy: vr 'self'

To request vr for iframes, you can
<iframe src="https://foo.bar" allow="vr"></iframe> to grant vr to this iframe.

Bug: 666767
Change-Id: I48d7accf8553d6a9ac19d9f41dbbce2ff9934579
Reviewed-on: https://chromium-review.googlesource.com/636663
Reviewed-by: Ian Vollick <vollick@chromium.org>
Reviewed-by: Michael Thiessen <mthiesse@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Reviewed-by: Ian Clelland <iclelland@chromium.org>
Commit-Queue: Biao She <bshe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#498674}
diff --git a/content/common/feature_policy/feature_policy.cc b/content/common/feature_policy/feature_policy.cc
index 6d82e7b..8255175 100644
--- a/content/common/feature_policy/feature_policy.cc
+++ b/content/common/feature_policy/feature_policy.cc
@@ -235,6 +235,8 @@
                            {blink::WebFeaturePolicyFeature::kSyncXHR,
                             FeaturePolicy::FeatureDefault::EnableForAll},
                            {blink::WebFeaturePolicyFeature::kUsb,
+                            FeaturePolicy::FeatureDefault::EnableForSelf},
+                           {blink::WebFeaturePolicyFeature::kWebVr,
                             FeaturePolicy::FeatureDefault::EnableForSelf}}));
   return default_feature_list;
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html
new file mode 100644
index 0000000..64a152b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/resources/feature-policy-webvr.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+Promise.resolve().then(() => navigator.getVRDisplays()).then(displays => {
+  window.parent.postMessage({ enabled: true }, '*');
+}, error => {
+  window.parent.postMessage({ enabled: false }, '*');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..567499c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr "none"';
+
+    promise_test(() => {
+      return navigator.getVRDisplays().then(() => {
+        assert_unreached('expected promise to reject');
+      }, error => {
+      });
+    }, header + ' disallows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..d021af7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
new file mode 100644
index 0000000..da01dafd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+  <script>
+    'use strict';
+    var relative_path = '/feature-policy/resources/feature-policy-webvr.html';
+    var base_src = '/feature-policy/resources/redirect-on-load.html#';
+    var same_origin_src = base_src + relative_path;
+    var cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
+        relative_path;
+    var header = 'Feature-Policy allow="vr" attribute';
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows same-origin relocation');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default, 'vr');
+    }, header + ' disallows cross-origin relocation');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
new file mode 100644
index 0000000..d715f90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy allow="vr" attribute';
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows same-origin iframe');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_available_default, 'vr');
+    }, header + ' allows cross-origin iframe');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..ee02566
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr *';
+
+    promise_test(
+        () => navigator.getVRDisplays(),
+        header + ' allows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..e7427ee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr *
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..bd7e82f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/feature-policy/resources/featurepolicy.js></script>
+
+  <script>
+    'use strict';
+    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
+    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    var header = 'Feature-Policy header vr "self"';
+
+    promise_test(
+        () => navigator.getVRDisplays(),
+        header + ' allows the top-level document.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability(
+          'navigator.getVRDisplays()', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..87d343d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: vr 'self'
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
index c966856..e13b967 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
@@ -18,6 +18,7 @@
 #include "modules/vr/VRDisplay.h"
 #include "modules/vr/VRGetDevicesCallback.h"
 #include "modules/vr/VRPose.h"
+#include "platform/feature_policy/FeaturePolicy.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/platform/Platform.h"
 
@@ -72,6 +73,23 @@
     return promise;
   }
 
+  LocalFrame* frame = GetDocument()->GetFrame();
+  // TODO(bshe): Add different error string for cases when promise is rejected.
+  if (!frame) {
+    RejectNavigatorDetached(resolver);
+    return promise;
+  }
+  if (IsSupportedInFeaturePolicy(WebFeaturePolicyFeature::kWebVr)) {
+    if (!frame->IsFeatureEnabled(WebFeaturePolicyFeature::kWebVr)) {
+      RejectNavigatorDetached(resolver);
+      return promise;
+    }
+  } else if (!frame->HasReceivedUserGesture() &&
+             frame->IsCrossOriginSubframe()) {
+    RejectNavigatorDetached(resolver);
+    return promise;
+  }
+
   UseCounter::Count(*GetDocument(), WebFeature::kVRGetDisplays);
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (!execution_context->IsSecureContext())
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
index 24a23c8..2a0f48a 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -189,6 +189,7 @@
     case WebFeaturePolicyFeature::kFullscreen:
     case WebFeaturePolicyFeature::kPayment:
     case WebFeaturePolicyFeature::kUsb:
+    case WebFeaturePolicyFeature::kWebVr:
       return true;
     case WebFeaturePolicyFeature::kVibrate:
       return RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled();
@@ -213,6 +214,7 @@
     default_feature_name_map.Set("geolocation",
                                  WebFeaturePolicyFeature::kGeolocation);
     default_feature_name_map.Set("midi", WebFeaturePolicyFeature::kMidiFeature);
+    default_feature_name_map.Set("vr", WebFeaturePolicyFeature::kWebVr);
     if (RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled()) {
       default_feature_name_map.Set("vibrate",
                                    WebFeaturePolicyFeature::kVibrate);
diff --git a/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h b/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
index 2c271ba..b67f918 100644
--- a/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
+++ b/third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
@@ -47,7 +47,9 @@
   kUsb,
   // Controls access to AOM event listeners.
   kAccessibilityEvents,
-  LAST_FEATURE = kAccessibilityEvents
+  // Controls use of WebVR API.
+  kWebVr,
+  LAST_FEATURE = kWebVr
 };
 
 }  // namespace blink