Merge pull request #15087 from web-platform-tests/tabatkins-patch-5

Inclusive Ranges
diff --git a/css/css-syntax/input-preprocessing.html b/css/css-syntax/input-preprocessing.html
new file mode 100644
index 0000000..9ef9a73
--- /dev/null
+++ b/css/css-syntax/input-preprocessing.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<title>Input Preprocessing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+foo { color: blue; }
+
+</style>
+
+<meta name="author" title="Tab Atkins-Bittner">
+<link rel=help href="https://drafts.csswg.org/css-syntax/#input-preprocessing">
+
+<script>
+
+function roundtripIdent(str) {
+    const rule = document.styleSheets[0].cssRules[0];
+    rule.selectorText = "original-ident";
+    rule.selectorText = str;
+    // Check for parse error.
+    if(rule.selectorText == "original-ident") return "parse error";
+    return rule.selectorText;
+}
+function testParsing(input, expected) {
+    test(()=>{
+        assert_equals(roundtripIdent(input), expected);
+    }, `"${input}" becomes "${expected}"`);
+}
+
+/* Can't figure out how to test the newline normalization... */
+
+/* NULL becomes FFFD */
+testParsing("foo\x00", "foo\ufffd");
+testParsing("f\x00oo", "f\ufffdoo");
+testParsing("\x00foo", "\ufffdfoo");
+testParsing("\x00", "\ufffd");
+testParsing("\x00\x00\x00", "\ufffd\ufffd\ufffd");
+
+/* surrogates become FFFD */
+testParsing("foo\ud800", "foo\ufffd");
+testParsing("f\ud800oo", "f\ufffdoo");
+testParsing("\ud800foo", "\ufffdfoo");
+testParsing("\ud800", "\ufffd");
+testParsing("\ud800\ud800\ud800", "\ufffd\ufffd\ufffd");
+
+</script>
diff --git a/css/css-syntax/unclosed-constructs.html b/css/css-syntax/unclosed-constructs.html
new file mode 100644
index 0000000..3d79403
--- /dev/null
+++ b/css/css-syntax/unclosed-constructs.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>Unclosed Constructs Are Valid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta name="author" title="Tab Atkins-Bittner">
+<link rel=help href="https://drafts.csswg.org/css-syntax/#rule-defs">
+
+<!--
+Tests that unclosed constructs are valid and match grammars,
+because grammar-matching only sees the "block",
+not the opening/closing characters themselves.
+-->
+
+<script>
+
+function validSelector(str) {
+    try {
+        document.querySelector(str);
+        return true;
+    } catch(e) {
+        return false;
+    }
+}
+function shouldBeValid(str) {
+    test(()=>{
+        assert_true(validSelector(str));
+    }, `"${str}" is a valid selector`)
+}
+
+shouldBeValid("[foo]");
+shouldBeValid("[foo");
+shouldBeValid(":nth-child(1)");
+shouldBeValid(":nth-child(1");
+
+</script>
diff --git a/feature-policy/reporting/xr-report-only.https.html b/feature-policy/reporting/xr-report-only.https.html
index b52bdbf..6844226 100644
--- a/feature-policy/reporting/xr-report-only.https.html
+++ b/feature-policy/reporting/xr-report-only.https.html
@@ -23,11 +23,11 @@
                           {types: ['feature-policy-violation']}).observe();
   });
   try {
-    await navigator.xr.requestDevice();
+    await navigator.xr.supportsSessionMode('inline');
   } catch (err) {
-    // If no XR devices are available, requestDevice() will throw NotFoundError,
-    // but the report should be generated anyway.
-    assert_equals(err.name, 'NotFoundError');
+    // If no XR devices are available, supportsSessionMode() will reject with a
+    // NotSupportedError, but the report should be generated anyway.
+    assert_equals(err.name, 'NotSupportedError');
   }
   check_report_format(await report);
 }, "XR report only mode");
diff --git a/feature-policy/reporting/xr-reporting.https.html b/feature-policy/reporting/xr-reporting.https.html
index b737bb9..1982ea5 100644
--- a/feature-policy/reporting/xr-reporting.https.html
+++ b/feature-policy/reporting/xr-reporting.https.html
@@ -22,7 +22,7 @@
     new ReportingObserver((reports, observer) => resolve([reports, observer]),
                           {types: ['feature-policy-violation']}).observe();
   });
-  await promise_rejects(t, 'SecurityError', navigator.xr.requestDevice(),
+  await promise_rejects(t, 'SecurityError', navigator.xr.supportsSessionMode('inline'),
                         "XR device access should not be allowed in this document.");
   const [reports, observer] = await report;
   check_report_format(reports, observer);
diff --git a/tools/tox.ini b/tools/tox.ini
index 3b0c6d1..975056e 100644
--- a/tools/tox.ini
+++ b/tools/tox.ini
@@ -15,9 +15,9 @@
   HYPOTHESIS_PROFILE
 
 [testenv:py27-flake8]
-deps = -r requirements_flake8.txt
-commands = flake8 --append-config=py27-flake8.ini {posargs}
+deps = -rrequirements_flake8.txt
+commands = flake8 --append-config={toxinidir}/py27-flake8.ini {posargs}
 
 [testenv:py36-flake8]
-deps = -r requirements_flake8.txt
-commands = flake8 --append-config=py36-flake8.ini {posargs}
+deps = -rrequirements_flake8.txt
+commands = flake8 --append-config={toxinidir}/py36-flake8.ini {posargs}
diff --git a/webxr/navigator_xr_requestDevice.https.html b/webxr/navigator_xr_requestDevice.https.html
deleted file mode 100644
index c51dd8d..0000000
--- a/webxr/navigator_xr_requestDevice.https.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-
-  <script>
-    xr_promise_test("navigator.xr.requestDevice returns a device", (t) => {
-      return XRTest.simulateDeviceConnection({ supportsImmersive: true })
-        .then( (controller) => { return navigator.xr.requestDevice() })
-        .then( (device) => {
-          t.step(() => {
-            assert_true(device != null);
-            assert_true(device instanceof XRDevice);
-          });
-        });
-    });
-  </script>
-</body>
diff --git a/webxr/navigator_xr_requestDevice_no_device.https.html b/webxr/navigator_xr_requestDevice_no_device.https.html
deleted file mode 100644
index 3cd149b..0000000
--- a/webxr/navigator_xr_requestDevice_no_device.https.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-
-  <script>
-    promise_test( (t) => {
-      return promise_rejects(t, 'NotFoundError', navigator.xr.requestDevice());
-    }, "navigator.xr.requestDevice properly rejects when there are 0 devices");
-  </script>
-</body>
diff --git a/webxr/resources/webxr_util.js b/webxr/resources/webxr_util.js
index 10bdc12..7344fa0 100644
--- a/webxr/resources/webxr_util.js
+++ b/webxr/resources/webxr_util.js
@@ -26,7 +26,6 @@
 // Requires a webglCanvas on the page.
 function xr_session_promise_test(
     name, func, fakeDeviceInit, sessionOptions, properties) {
-  let testDevice;
   let testDeviceController;
   let testSession;
 
@@ -44,16 +43,12 @@
           XRTest.simulateDeviceConnection(fakeDeviceInit)
               .then((controller) => {
                 testDeviceController = controller;
-                return navigator.xr.requestDevice();
-              })
-              .then((device) => {
-                testDevice = device;
                 return gl.makeXRCompatible();
               })
               .then(() => new Promise((resolve, reject) => {
                       // Perform the session request in a user gesture.
                       XRTest.simulateUserActivation(() => {
-                        testDevice.requestSession(sessionOptions)
+                        navigator.xr.requestSession(sessionOptions)
                             .then((session) => {
                               testSession = session;
                               // Session must have a baseLayer or frame requests
@@ -74,7 +69,7 @@
               .then(() => {
                 // Cleanup system state.
                 testSession.end().catch(() => {});
-                XRTest.simulateDeviceDisconnection(testDevice);
+                XRTest.simulateDeviceDisconnection();
               }),
       properties);
 }
@@ -97,7 +92,6 @@
 //                that API object.
 function forEachWebxrObject(callback) {
   callback(window.navigator.xr, 'navigator.xr');
-  callback(window.XRDevice, 'XRDevice');
   callback(window.XRSession, 'XRSession');
   callback(window.XRSessionCreationOptions, 'XRSessionCreationOptions');
   callback(window.XRFrameRequestCallback, 'XRFrameRequestCallback');
diff --git a/webxr/xrDevice_requestSession_immersive.https.html b/webxr/xrDevice_requestSession_immersive.https.html
index e9e7b9b..c5956e7 100644
--- a/webxr/xrDevice_requestSession_immersive.https.html
+++ b/webxr/xrDevice_requestSession_immersive.https.html
@@ -9,6 +9,6 @@
       "Tests requestSession resolves when supported",
       (session) => {
         assert_not_equals(session, null);
-      }, { supportsImmersive:true }, { immersive: true });
+      }, { supportsImmersive:true }, { mode: 'immersive-vr' });
   </script>
 </body>
\ No newline at end of file
diff --git a/webxr/xrDevice_requestSession_immersive_no_gesture.https.html b/webxr/xrDevice_requestSession_immersive_no_gesture.https.html
index 9f0f5f3..c1b3286 100644
--- a/webxr/xrDevice_requestSession_immersive_no_gesture.https.html
+++ b/webxr/xrDevice_requestSession_immersive_no_gesture.https.html
@@ -8,9 +8,8 @@
       "Requesting immersive session outside of a user gesture rejects",
       (t) => {
         return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-          .then( (controller) => { return navigator.xr.requestDevice() })
-          .then( (device) => promise_rejects(
-            t, 'SecurityError', device.requestSession({ immersive: true })));
+          .then( (controller) => promise_rejects(
+            t, 'SecurityError', navigator.xr.requestSession({ mode: 'immersive-vr' })));
       });
   </script>
 </body>
diff --git a/webxr/xrDevice_requestSession_immersive_unsupported.https.html b/webxr/xrDevice_requestSession_immersive_unsupported.https.html
index e0d2688..60e9e6c 100644
--- a/webxr/xrDevice_requestSession_immersive_unsupported.https.html
+++ b/webxr/xrDevice_requestSession_immersive_unsupported.https.html
@@ -8,13 +8,12 @@
       "Requesting an immersive session when unsupported rejects",
       (t) => {
         return XRTest.simulateDeviceConnection({ supportsImmersive:false })
-          .then( (controller) => { return navigator.xr.requestDevice() })
-          .then( (magicWindowOnlyDevice) => new Promise((resolve) => {
+          .then( (controller) => new Promise((resolve) => {
             XRTest.simulateUserActivation( () => {
               resolve(promise_rejects(
                 t,
                 "NotSupportedError",
-                magicWindowOnlyDevice.requestSession({ immersive: true })
+                navigator.xr.requestSession({ mode: 'immersive-vr' })
               ))
             });
           }));
diff --git a/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html b/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html
index 1634dfe..6cf50f5 100644
--- a/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html
+++ b/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html
@@ -8,9 +8,8 @@
       "Requesting non-immersive session outside of a user gesture succeeds",
       (t) => {
         return XRTest.simulateDeviceConnection({ supportsImmersive:false })
-          .then( (controller) => { return navigator.xr.requestDevice(); })
-          .then( (device) => device.requestSession({
-            immersive: false,
+          .then( (controller) => navigator.xr.requestSession({
+            mode: 'inline',
             outputContext: getOutputContext()
           }))
           .then( (session) => { assert_not_equals(session, null); });
diff --git a/webxr/xrDevice_supportsSession_immersive.https.html b/webxr/xrDevice_supportsSession_immersive.https.html
index 3ca5eb1..c0b4701 100644
--- a/webxr/xrDevice_supportsSession_immersive.https.html
+++ b/webxr/xrDevice_supportsSession_immersive.https.html
@@ -5,11 +5,10 @@
   <script src="resources/webxr_util.js"></script>
   <script>
     xr_promise_test(
-      "supportsSession resolves when immersive options supported",
+      "supportsSessionMode resolves when immersive options supported",
       () => {
         return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-          .then( (controller) => { return navigator.xr.requestDevice() })
-          .then( (device) => device.supportsSession({ immersive: true }));
+          .then( (controller) => navigator.xr.supportsSessionMode('immersive-vr'));
       });
   </script>
 </body>
diff --git a/webxr/xrDevice_supportsSession_immersive_unsupported.https.html b/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
index 0c1dd25..2b17b96 100644
--- a/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
+++ b/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
@@ -5,15 +5,14 @@
   <script src="resources/webxr_util.js"></script>
   <script>
     xr_promise_test(
-      "supportsSession rejects when options not supported",
+      "supportsSessionMode rejects when options not supported",
       (t) => {
       return XRTest.simulateDeviceConnection({ supportsImmersive:false })
-        .then( (controller) => { return navigator.xr.requestDevice() })
-        .then( (device) => {
+        .then( (controller) => {
           return promise_rejects(
             t,
             "NotSupportedError",
-            device.supportsSession({ immersive: true })
+            navigator.xr.supportsSessionMode('immersive-vr')
           );
         });
     });
diff --git a/webxr/xrDevice_supportsSession_non_immersive.https.html b/webxr/xrDevice_supportsSession_non_immersive.https.html
index 535b0bc..69a35c2 100644
--- a/webxr/xrDevice_supportsSession_non_immersive.https.html
+++ b/webxr/xrDevice_supportsSession_non_immersive.https.html
@@ -5,18 +5,12 @@
   <script src="resources/webxr_util.js"></script>
   <script>
     xr_promise_test(
-      "supportsSession resolves when non-immersive options supported",
+      "supportsSessionMode resolves when inline options supported",
       (t) => {
       return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-        .then( (controller) => { return navigator.xr.requestDevice() })
-        .then( (device) => {
-          // Non-immersive sessions without a outputContext should not be supported.
-          promise_rejects(t, 'NotSupportedError', device.supportsSession());
-
-          // Non-immersive sessions with an outputContext should be supported.
-          return device.supportsSession({
-              outputContext: getOutputContext()
-          });
+        .then( (controller) => {
+          // Inline sessions should be supported.
+          return navigator.xr.supportsSessionMode('inline');
         });
     });
   </script>
diff --git a/webxr/xrSession_cancelAnimationFrame.https.html b/webxr/xrSession_cancelAnimationFrame.https.html
index 26c0e95..9b08f93 100644
--- a/webxr/xrSession_cancelAnimationFrame.https.html
+++ b/webxr/xrSession_cancelAnimationFrame.https.html
@@ -13,7 +13,7 @@
 
     let fakeDeviceInitParams = { supportsImmersive:true };
 
-    let immersiveSessionOptions = { immersive: true };
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
     let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     let testFunction = (session) => new Promise((resolve, reject) => {
diff --git a/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html b/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html
index 4f4b8df..bc9b625 100644
--- a/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html
+++ b/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html
@@ -12,7 +12,7 @@
 
     let fakeDeviceInitParams = { supportsImmersive:true };
 
-    let immersiveSessionOptions = { immersive: true };
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
     let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     let testFunction = (testSession) => new Promise((resolve) => {
diff --git a/webxr/xrSession_end.https.html b/webxr/xrSession_end.https.html
index 69bbda8..2719bdd 100644
--- a/webxr/xrSession_end.https.html
+++ b/webxr/xrSession_end.https.html
@@ -11,7 +11,7 @@
     let watcherDone = new Event("watcherdone");
     const fakeDeviceInitParams = { supportsImmersive:true };
 
-    const immersiveSessionOptions = { immersive: true };
+    const immersiveSessionOptions = { mode: 'immersive-vr' };
     const nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     let testFunction = function(session, testDeviceController, t) {
diff --git a/webxr/xrSession_mode.https.html b/webxr/xrSession_mode.https.html
new file mode 100644
index 0000000..4b54d94
--- /dev/null
+++ b/webxr/xrSession_mode.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    xr_promise_test("Requested session has it's mode set",
+     (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => new Promise((resolve) => {
+          XRTest.simulateUserActivation( () => {
+            resolve(navigator.xr.requestSession({ mode: 'immersive-vr' }).then( (session) => {
+              assert_equals(session.mode, 'immersive-vr');
+            }));
+          });
+        }));
+    });
+  </script>
+</body>
+
diff --git a/webxr/xrSession_prevent_multiple_exclusive.https.html b/webxr/xrSession_prevent_multiple_exclusive.https.html
index ff3eaef..c2e7f3b 100644
--- a/webxr/xrSession_prevent_multiple_exclusive.https.html
+++ b/webxr/xrSession_prevent_multiple_exclusive.https.html
@@ -10,26 +10,25 @@
       "Test prevention of multiple simultaneous immersive sessions",
       (t) => {
       return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-        .then( (controller) => { return navigator.xr.requestDevice() })
-        .then( (device) => new Promise((resolve) => {
+        .then( (controller) => new Promise((resolve) => {
           XRTest.simulateUserActivation( () => {
-            resolve(device.requestSession({ immersive: true })
+            resolve(navigator.xr.requestSession({ mode: 'immersive-vr' })
               .then( (session) => new Promise((resolve) => {
                 XRTest.simulateUserActivation( () => {
-                  // Requesting a second immersive session from a device that already
-                  // has an active immersive session should fail. Immersive sessions
+                  // Requesting a second immersive session when another immersive
+                  // session is active should fail. Immersive sessions
                   // should take up the users entire view, and therefore it should
                   // be impossible for a user to be engaged with more than one.
                   resolve(promise_rejects(
                     t,
                     "InvalidStateError",
-                    device.requestSession({ immersive: true })
+                    navigator.xr.requestSession({ mode: 'immersive-vr' })
                   ).then( () => {
                       // End the immersive session and try again. Now the immersive
                       // session creation should succeed.
                       return session.end().then( () => new Promise((resolve) => {
                         XRTest.simulateUserActivation( () => {
-                          resolve(device.requestSession({ immersive: true }));
+                          resolve(navigator.xr.requestSession({ mode: 'immersive-vr' }));
                         });
                       }));
                     }));
diff --git a/webxr/xrSession_requestAnimationFrame_callback_calls.https.html b/webxr/xrSession_requestAnimationFrame_callback_calls.https.html
index 48dc887..268efcd 100644
--- a/webxr/xrSession_requestAnimationFrame_callback_calls.https.html
+++ b/webxr/xrSession_requestAnimationFrame_callback_calls.https.html
@@ -13,7 +13,7 @@
 
     let fakeDeviceInitParams = { supportsImmersive:true };
 
-    let immersiveSessionOptions = { immersive: true };
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
     let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     let testFunction = (testSession) => new Promise((resolve) => {
diff --git a/webxr/xrSession_requestAnimationFrame_data_valid.https.html b/webxr/xrSession_requestAnimationFrame_data_valid.https.html
index b5f4f18..f873a11 100644
--- a/webxr/xrSession_requestAnimationFrame_data_valid.https.html
+++ b/webxr/xrSession_requestAnimationFrame_data_valid.https.html
@@ -28,7 +28,7 @@
     };
 
     const fakeDeviceInitOptions = { supportsImmersive:true };
-    const sessionOptions = { immersive:true };
+    const sessionOptions = { mode: 'immersive-vr' };
 
     let testSession;
 
diff --git a/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html b/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html
index 17b5307..ca6a717 100644
--- a/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html
+++ b/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html
@@ -14,7 +14,7 @@
 
     let fakeDeviceInitParams = { supportsImmersive: true };
 
-    let immersiveSessionOptions = { immersive: true };
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
     let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     // Valid matrices for  when we don't care about specific values
diff --git a/webxr/xrSession_requestReferenceSpace.https.html b/webxr/xrSession_requestReferenceSpace.https.html
index d97852c..1eb49e0 100644
--- a/webxr/xrSession_requestReferenceSpace.https.html
+++ b/webxr/xrSession_requestReferenceSpace.https.html
@@ -13,7 +13,7 @@
 
     let fakeDeviceInitParams = { supportsImmersive: true };
 
-    let immersiveSessionOptions = { immersive: true };
+    let immersiveSessionOptions = { mode: 'immersive-vr' };
     let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
 
     let testFunction = function(session, fakeDeviceController, t) {