Merge pull request #15029 from TalbotG/Values3-GT-PR4

Added 7 new css-values tests and 2 reference files
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index 387c55a..acfb099 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -6,9 +6,13 @@
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/index
 #
-# In addition to this configuration file, the "Build pull requests from forks
-# of this repository" setting must also be enabled in the Azure DevOps project:
-# https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github#validate-contributions-from-forks
+# In addition to this configuration file, some setup in the Azure DevOps
+# project is required:
+#  - The "Build pull requests from forks of this repository" setting must be
+#    enabled: https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github#validate-contributions-from-forks
+#  - A scheduled build needs to be set up for the the epochs/daily branch.
+#  - To get results from scheduled builds into wpt.fyi, a service connection
+#    named wpt.fyi with URL https://wpt.fyi is needed.
 
 jobs:
 # The affected tests jobs are unconditional for speed, as most PRs have one or
@@ -125,7 +129,7 @@
 
 - job: all_macOS
   displayName: 'all tests (Safari Technology Preview)'
-  condition: eq(variables['Build.SourceBranch'], 'refs/heads/epochs/daily')
+  condition: eq(variables['Build.Reason'], 'Schedule')
   strategy:
     parallel: 4 # chosen to make runtime ~2h
   timeoutInMinutes: 360
@@ -148,3 +152,20 @@
     inputs:
       artifactName: 'results'
     condition: succeededOrFailed()
+
+# The InvokeRESTAPI task can only run in a server job.
+- job: all_post
+  displayName: 'all tests (wpt.fyi hook)'
+  dependsOn: all_macOS
+  pool: server
+  steps:
+  - task: InvokeRESTAPI@1
+    displayName: 'Invoke wpt.fyi hook'
+    inputs:
+      serviceConnection: wpt.fyi
+      urlSuffix: /api/checks/azure/$(Build.BuildId)
+  - task: InvokeRESTAPI@1
+    displayName: 'Invoke staging.wpt.fyi hook'
+    inputs:
+      serviceConnection: staging.wpt.fyi
+      urlSuffix: /api/checks/azure/$(Build.BuildId)
diff --git a/.taskcluster.yml b/.taskcluster.yml
index 4a6de3b..ae99c09 100644
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -61,7 +61,7 @@
               owner: ${event.pusher.email}
               source: ${event.repository.url}
             payload:
-              image: harjgam/web-platform-tests:0.25
+              image: harjgam/web-platform-tests:0.26
               maxRunTime: 7200
               artifacts:
                 public/results:
@@ -136,7 +136,7 @@
                 owner: ${event.pull_request.user.login}@users.noreply.github.com
                 source: ${event.repository.url}
               payload:
-                image: harjgam/web-platform-tests:0.25
+                image: harjgam/web-platform-tests:0.26
                 maxRunTime: 7200
                 artifacts:
                   public/results:
diff --git a/IndexedDB/idb-explicit-commit.any.js b/IndexedDB/idb-explicit-commit.any.js
index da4bd8c..12e56cc 100644
--- a/IndexedDB/idb-explicit-commit.any.js
+++ b/IndexedDB/idb-explicit-commit.any.js
@@ -188,20 +188,17 @@
   });
   // Txn1 should commit before txn2, even though txn2 uses commit().
   const txn1 = db.transaction(['books'], 'readwrite');
-  const objectStore1 = txn1.objectStore('books');
-  const putRequest1 = objectStore1.put({isbn:'one', title:'title1'});
+  txn1.objectStore('books').put({isbn: 'one', title: 'title1'});
   const releaseTxnFunction = keepAlive(testCase, txn1, 'books');
 
   const txn2 = db.transaction(['books'], 'readwrite');
-  const objectStore2 = txn2.objectStore('books');
-  const putRequest2 = objectStore2.put({isbn:'one', title:'title2'});
+  txn2.objectStore('books').put({isbn:'one', title:'title2'});
   txn2.commit();
 
   // Exercise the IndexedDB transaction ordering by executing one with a
   // different scope.
   const txn3 = db.transaction(['not_books'], 'readwrite');
-  const objectStore3 = txn3.objectStore('not_books');
-  objectStore3.put({'title': 'not_title'}, 'key');
+  txn3.objectStore('not_books').put({'title': 'not_title'}, 'key');
   txn3.oncomplete = function() {
     releaseTxnFunction();
   }
@@ -210,8 +207,7 @@
 
   // Read the data back to verify that txn2 executed last.
   const txn4 = db.transaction(['books'], 'readonly');
-  const objectStore4 = txn4.objectStore('books');
-  const getRequest4 = objectStore4.get('one');
+  const getRequest4 = txn4.objectStore('books').get('one');
   await promiseForTransaction(testCase, txn4);
   assert_equals(getRequest4.result.title, 'title2');
   db.close();
@@ -225,8 +221,7 @@
   });
   // Txn1 creates the book 'one' so the 'add()' below fails.
   const txn1 = db.transaction(['books'], 'readwrite');
-  const objectStore1 = txn1.objectStore('books');
-  const putRequest1 = objectStore1.add({isbn:'one', title:'title1'});
+  txn1.objectStore('books').add({isbn:'one', title:'title1'});
   txn1.commit();
   await promiseForTransaction(testCase, txn1);
 
@@ -235,15 +230,17 @@
   const txn2 = db.transaction(['books'], 'readwrite');
   const objectStore2 = txn2.objectStore('books');
   objectStore2.put({isbn:'two', title:'title2'});
-  const addRequest2 = objectStore2.add({isbn:'one', title:'title2'});
+  const addRequest = objectStore2.add({isbn:'one', title:'title2'});
   txn2.commit();
-  txn2.oncomplete = assert_unreached(
-    'Transaction with invalid "add" call should not be completed.');
+  txn2.oncomplete = () => { assert_unreached(
+    'Transaction with invalid "add" call should not be completed.'); };
 
-  var addWatcher = requestWatcher(testCase, addRequest2);
-  var txnWatcher = transactionWatcher(testCase, txn2);
-  await Promise.all([addWatcher.wait_for('error'),
-                     txnWatcher.wait_for('error', 'abort')]);
+  // Wait for the transaction to complete. We have to explicitly wait for the
+  // error signal on the transaction because of the nature of the test tooling.
+  await Promise.all([
+      requestWatcher(testCase, addRequest).wait_for('error'),
+      transactionWatcher(testCase, txn2).wait_for(['error', 'abort'])
+  ]);
 
   // Read the data back to verify that txn2 was aborted.
   const txn3 = db.transaction(['books'], 'readonly');
@@ -255,3 +252,41 @@
   assert_equals(getRequest2.result, 0);
   db.close();
 }, 'Transactions that explicitly commit and have errors should abort.');
+
+
+promise_test(async testCase => {
+  const db = await createDatabase(testCase, db => {
+    createBooksStore(testCase, db);
+  });
+  const txn1 = db.transaction(['books'], 'readwrite');
+  txn1.objectStore('books').add({isbn: 'one', title: 'title1'});
+  txn1.commit();
+  await promiseForTransaction(testCase, txn1);
+
+  // The second add request will throw an error, but the onerror handler will
+  // appropriately catch the error allowing the valid put request on the
+  // transaction to commit.
+  const txn2 = db.transaction(['books'], 'readwrite');
+  const objectStore2 = txn2.objectStore('books');
+  objectStore2.put({isbn: 'two', title:'title2'});
+  const addRequest = objectStore2.add({isbn: 'one', title:'unreached_title'});
+  addRequest.onerror = (event) => {
+    event.preventDefault();
+    addRequest.transaction.commit();
+  };
+
+  // Wait for the transaction to complete. We have to explicitly wait for the
+  // error signal on the transaction because of the nature of the test tooling.
+  await transactionWatcher(testCase,txn2).wait_for(['error', 'complete'])
+
+  // Read the data back to verify that txn2 was committed.
+  const txn3 = db.transaction(['books'], 'readonly');
+  const objectStore3 = txn3.objectStore('books');
+  const getRequest1 = objectStore3.get('one');
+  const getRequest2 = objectStore3.get('two');
+  await promiseForTransaction(testCase, txn3);
+  assert_equals(getRequest1.result.title, 'title1');
+  assert_equals(getRequest2.result.title, 'title2');
+  db.close();
+}, 'Transactions that handle all errors properly should be behave as ' +
+   'expected when an explicit commit is called in an onerror handler.');
diff --git a/accelerometer/Accelerometer-supported-by-feature-policy.html b/accelerometer/Accelerometer-supported-by-feature-policy.html
new file mode 100644
index 0000000..8e09c7a
--- /dev/null
+++ b/accelerometer/Accelerometer-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that accelerometer is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/sensors/#feature-policy-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('accelerometer', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise accelerometer.');
+</script>
diff --git a/ambient-light/AmbientLightSensor-supported-by-feature-policy.html b/ambient-light/AmbientLightSensor-supported-by-feature-policy.html
new file mode 100644
index 0000000..d5c27c8
--- /dev/null
+++ b/ambient-light/AmbientLightSensor-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that ambient-light-sensor is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/sensors/#feature-policy-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('ambient-light-sensor', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise ambient-light-sensor.');
+</script>
diff --git a/animation-worklet/common.js b/animation-worklet/common.js
index eb114f2..15dff82 100644
--- a/animation-worklet/common.js
+++ b/animation-worklet/common.js
@@ -30,3 +30,9 @@
   // AnimationWorklet.
   return waitForAnimationFrames(count + 1);
 }
+
+async function waitForAnimationFrameWithCondition(condition) {
+  do {
+    await new Promise(window.requestAnimationFrame);
+  } while (!condition())
+};
diff --git a/animation-worklet/references/translated-box-ref.html b/animation-worklet/references/translated-box-ref.html
new file mode 100644
index 0000000..c73f5a1
--- /dev/null
+++ b/animation-worklet/references/translated-box-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  transform: translateY(100px);
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
diff --git a/animation-worklet/worklet-animation-pause-immediately.https.html b/animation-worklet/worklet-animation-pause-immediately.https.html
new file mode 100644
index 0000000..f9dcf30
--- /dev/null
+++ b/animation-worklet/worklet-animation-pause-immediately.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Verify that calling pause immediately after playing works as expected</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<link rel="match" href="references/translated-box-ref.html">
+
+<script src="/common/reftest-wait.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
+
+<script>
+registerPassthroughAnimator().then(async _ => {
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(100px)', 'translateY(200px)'] },
+    { duration: 100, iterations: 1 }
+  );
+
+  const animation = new WorkletAnimation('passthrough', effect);
+  animation.play();
+  // Immediately pausing animation should freeze the current time at 0.
+  animation.pause();
+  // Wait at least one frame to ensure a paused animation actually freezes.
+  await waitForAsyncAnimationFrames(1);
+  takeScreenshot();
+});
+</script>
+</html>
diff --git a/animation-worklet/worklet-animation-pause-resume.https.html b/animation-worklet/worklet-animation-pause-resume.https.html
new file mode 100644
index 0000000..f26a934
--- /dev/null
+++ b/animation-worklet/worklet-animation-pause-resume.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Verify that calling pause immediately after playing works as expected</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<link rel="match" href="references/translated-box-ref.html">
+
+<script src="/common/reftest-wait.js"></script>
+<script src="common.js"></script>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
+
+<script>
+registerPassthroughAnimator().then(async _  => {
+  const duration = 18; // a bit longer than a frame
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(0px)', 'translateY(100px)'] },
+    { duration: duration, iterations: 1, fill: 'forwards'}
+  );
+
+  const animation = new WorkletAnimation('passthrough', effect);
+  // Immediately pausing animation should freeze the current time at 0.
+  animation.pause();
+  // Playing should cause animation to resume.
+  animation.play();
+  // Wait until we ensure animation has reached completion.
+  await waitForAnimationFrameWithCondition( _ => {
+    return animation.currentTime >= duration;
+  });
+  takeScreenshot();
+});
+</script>
+</html>
diff --git a/animation-worklet/worklet-animation-pause.https.html b/animation-worklet/worklet-animation-pause.https.html
new file mode 100644
index 0000000..4b7a9a0
--- /dev/null
+++ b/animation-worklet/worklet-animation-pause.https.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Verify that currentTime and playState are correct when animation is paused</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+
+<div id="box"></div>
+
+<script>
+
+setup(setupAndRegisterTests, {explicit_done: true});
+
+function createAnimation() {
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(100px)', 'translateY(200px)'] },
+    { duration: 100, iterations: 1 }
+  );
+
+  return new WorkletAnimation('passthrough', effect);
+}
+
+async function setupAndRegisterTests() {
+  await registerPassthroughAnimator();
+
+  promise_test(async t => {
+    const animation = createAnimation();
+    animation.play();
+    // Immediately pausing animation should freeze the current time at 0.
+    animation.pause();
+    assert_equals(animation.currentTime, 0);
+    assert_equals(animation.playState, "paused");
+    // Wait some time to ensure a paused animation actually freezes.
+    await waitForNextFrame();
+    assert_equals(animation.currentTime, 0);
+    assert_equals(animation.playState, "paused");
+  }, 'pausing an animation freezes its current time');
+
+  promise_test(async t => {
+    const animation = createAnimation();
+    const startTime = document.timeline.currentTime;
+    animation.pause();
+    animation.play();
+    await waitForNextFrame();
+    const timelineTime = document.timeline.currentTime;
+    assert_equals(animation.playState, "running");
+    assert_greater_than(animation.currentTime, 0);
+    assert_times_equal(animation.currentTime, (timelineTime - startTime));
+  }, 'playing a paused animation should resume it');
+
+  done();
+}
+
+</script>
+
diff --git a/background-fetch/fetch-uploads.https.window.js b/background-fetch/fetch-uploads.https.window.js
index 4ee618a..f93f88a 100644
--- a/background-fetch/fetch-uploads.https.window.js
+++ b/background-fetch/fetch-uploads.https.window.js
@@ -44,3 +44,21 @@
 
   assert_equals(uploaded, uploadData.length);
 }, 'Progress event includes uploaded bytes');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const uploadRequest1 =
+      new Request('resources/upload.py', {method: 'POST', body: 'upload1'});
+  const uploadRequest2 =
+      new Request('resources/upload.py', {method: 'POST', body: 'upload2'});
+
+  await backgroundFetch.fetch(uniqueId(), [uploadRequest1, uploadRequest2]);
+
+  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
+  assert_equals(type, 'backgroundfetchsuccess');
+  assert_equals(results.length, 2);
+  assert_equals(eventRegistration.result, 'success');
+  assert_equals(eventRegistration.failureReason, '');
+
+  assert_array_equals([results[0].text, results[1].text].sort(),
+                      ['upload1', 'upload2']);
+}, 'Duplicate upload requests work and can be distinguished.');
diff --git a/css/css-shadow-parts/interaction-with-pseudo-elements.html b/css/css-shadow-parts/interaction-with-pseudo-elements.html
new file mode 100644
index 0000000..776ab2e
--- /dev/null
+++ b/css/css-shadow-parts/interaction-with-pseudo-elements.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Interaction with pseudo-elements</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>
+      #c-e::part(before-p)::before { color: green; }
+      #c-e::part(after-p)::after { color: green; }
+      #c-e::part(placeholder-p)::placeholder { color: green; }
+      #c-e::part(selection-p)::selection { color: green; }
+      #c-e::part(first-line-p)::first-line { color: green; }
+      #c-e::part(first-letter-p)::first-letter { color: green; }
+    </style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>
+        #before-i::before { content: "this text"; color: red; }
+        #after-i::after { content: "this text"; color: red; }
+        #placeholder-i::placeholder { color: red; }
+        #selection-i::selection { color: red; }
+        #first-line-i::first-line { color: red; }
+        #first-letter-i::first-letter { color: red; }
+      </style>
+      <div>
+        The following text should be green:
+        <span id="before-i" part="before-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <span id="after-i" part="after-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
+      </div>
+      <div>
+        The selected text should be green:
+        <div id="selection-i" part="selection-p">select some text</div>
+      </div>
+      <div>
+        The following text should be green:
+        <div id="first-line-i" part="first-line-p">this text<br>Not this</div>
+      </div>
+      <div>
+        The first letter should be green:
+        <div id="first-letter-i" part="first-letter-p"><p>this text</p></div>
+      </div>
+    </template>
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorGreen = "rgb(0, 128, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "before-i"]);
+        assert_equals(window.getComputedStyle(el, '::before').color, colorGreen);
+      }, "::before in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "after-i"]);
+        assert_equals(window.getComputedStyle(el, '::after').color, colorGreen);
+      }, "::after in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "placeholder-i"]);
+        assert_equals(window.getComputedStyle(el, '::placeholder').color, colorGreen);
+      }, "::placeholder in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "selection-i"]);
+        assert_equals(window.getComputedStyle(el, '::selection').color, colorGreen);
+      }, "::selection in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "first-line-i"]);
+        assert_equals(window.getComputedStyle(el, '::first-line').color, colorGreen);
+      }, "::first-line in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "first-letter-i"]);
+        assert_equals(window.getComputedStyle(el, '::first-letter').color, colorGreen);
+      }, "::first-letter in selected host is styled");
+    </script>
+  </body>
+</html>
diff --git a/css/css-shadow-parts/interaction-with-tree-abiding.html b/css/css-shadow-parts/interaction-with-tree-abiding.html
deleted file mode 100644
index c11da7d..0000000
--- a/css/css-shadow-parts/interaction-with-tree-abiding.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>CSS Shadow Parts - Interaction with tree-abiding</title>
-    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
-    <link href="http://www.google.com/" rel="author" title="Google">
-    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
-    <script src="/resources/testharness.js"></script>
-    <script src="/resources/testharnessreport.js"></script>
-    <script src="support/shadow-helper.js"></script>
-  </head>
-  <body>
-    <style>
-      #c-e::part(before-p)::before { color: green; }
-      #c-e::part(after-p)::after { color: green; }
-      #c-e::part(placeholder-p)::placeholder { color: green; }
-    </style>
-    <script>installCustomElement("custom-element", "custom-element-template");</script>
-    <template id="custom-element-template">
-      <style>
-        #before-i::before { content: "this text"; color: red; }
-        #after-i::after { content: "this text"; color: red; }
-        #placeholder-i::placeholder { color: red; }
-      </style>
-      <div>
-        The following text should be green:
-        <span id="before-i" part="before-p"></span>
-      </div>
-      <div>
-        The following text should be green:
-        <span id="after-i" part="after-p"></span>
-      </div>
-      <div>
-        The following text should be green:
-        <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
-      </div>
-    </template>
-    <custom-element id="c-e"></custom-element>
-    <script>
-      "use strict";
-      const colorGreen = "rgb(0, 128, 0)";
-      test(function() {
-        const el = getElementByShadowIds(document, ["c-e", "before-i"]);
-        assert_equals(window.getComputedStyle(el, '::before').color, colorGreen);
-      }, "::before in selected host is styled");
-      test(function() {
-        const el = getElementByShadowIds(document, ["c-e", "after-i"]);
-        assert_equals(window.getComputedStyle(el, '::after').color, colorGreen);
-      }, "::after in selected host is styled");
-      test(function() {
-        const el = getElementByShadowIds(document, ["c-e", "placeholder-i"]);
-        assert_equals(window.getComputedStyle(el, '::placeholder').color, colorGreen);
-      }, "::placeholder in selected host is styled");
-    </script>
-  </body>
-</html>
diff --git a/css/css-text/overflow-wrap/overflow-wrap-break-word-002.html b/css/css-text/overflow-wrap/overflow-wrap-break-word-002.html
index 660b7f1..6f90f0c 100644
--- a/css/css-text/overflow-wrap/overflow-wrap-break-word-002.html
+++ b/css/css-text/overflow-wrap/overflow-wrap-break-word-002.html
@@ -22,7 +22,7 @@
 </style>
 
 <p>This test passes if there is nothing below this sentence.
-<div> FAIL <div>
+<div> FAIL </div>
 <!--
 white-space:break-spaces should cause the spaces at the end of the line to be preserved.
 Since there is an allowed break point between the first space and the F,
diff --git a/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html b/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html
index 6203b55..05e570b 100644
--- a/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html
+++ b/css/css-text/overflow-wrap/overflow-wrap-break-word-003.html
@@ -22,4 +22,4 @@
 </style>
 
 <p>This test passes if the word FAIL does not appear below.
-<div>PASS FAIL<div>
+<div>PASS FAIL</div>
diff --git a/css/css-text/overflow-wrap/overflow-wrap-break-word-004.html b/css/css-text/overflow-wrap/overflow-wrap-break-word-004.html
index b277319..5f36410 100644
--- a/css/css-text/overflow-wrap/overflow-wrap-break-word-004.html
+++ b/css/css-text/overflow-wrap/overflow-wrap-break-word-004.html
@@ -3,6 +3,7 @@
 <title>CSS Text Test: overflow-wrap: break-word</title>
 <link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-break-word">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
 <meta name="flags" content="ahem">
 <link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
 <meta name="assert" content="A Single leading white-space constitutes a soft breaking opportunity, honoring the 'white-space: pre-wrap' property, that must prevent the word to be broken.">
diff --git a/css/css-text/overflow-wrap/overflow-wrap-break-word-006.html b/css/css-text/overflow-wrap/overflow-wrap-break-word-006.html
new file mode 100644
index 0000000..6dc1b4d
--- /dev/null
+++ b/css/css-text/overflow-wrap/overflow-wrap-break-word-006.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: break-word</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-break-word">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<meta name="assert" content="A Single leading white-space constitutes a soft breaking opportunity, honoring the 'white-space: break-spaces' property, that must prevent the word to be broken.">
+<style>
+div {
+   position: relative;
+   font-size: 20px;
+   font-family: Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  line-height: 1em;
+  width: 5ch;
+  white-space: break-spaces;
+  overflow-wrap: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"><br>XXXXX</div>
+  <div class="test"> XXXXX </div>
+</body>
diff --git a/css/css-text/white-space/break-spaces-004.html b/css/css-text/white-space/break-spaces-004.html
new file mode 100644
index 0000000..e2c043e
--- /dev/null
+++ b/css/css-text/white-space/break-spaces-004.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space: break-spaces</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-word">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/pre-wrap-001-ref.html">
+<meta name="assert" content="The word is not broken if there are previous breaking opportunities, honoring the 'white-space: break-spaces' value.">
+<style>
+div {
+   position: relative;
+   font: 20px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 2ch;
+
+  white-space: break-spaces;
+  word-break: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail"><span>XX</span><br>XX</div>
+  <div class="test"> XX</div>
+</body>
diff --git a/css/css-text/white-space/break-spaces-005.html b/css/css-text/white-space/break-spaces-005.html
new file mode 100644
index 0000000..d0dafd3
--- /dev/null
+++ b/css/css-text/white-space/break-spaces-005.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space - break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="match" href="reference/white-space-break-spaces-005-ref.html">
+<meta name="flags" content="ahem">
+<meta name="assert" content="If 'white-space' is set to 'break-spaces', collapsing preserved white-spaces' advance width is not allowed, so that they can be wrapped honoring the 'white-space' propery.">
+<style>
+div {
+   position: relative;
+   font: 10px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 100px;
+
+  white-space: break-spaces;
+}
+
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail">XXXX<span>XXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XXXXXXXXXX</span><br><span>XX</span>XXXX<span>XXXX</span></div>
+  <div class="test">XXXX                                                                                        XXXX</div>
+</body>
diff --git a/css/css-text/white-space/break-spaces-006.html b/css/css-text/white-space/break-spaces-006.html
new file mode 100644
index 0000000..5f9d605
--- /dev/null
+++ b/css/css-text/white-space/break-spaces-006.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space - break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="match" href="reference/white-space-break-spaces-005-ref.html">
+<meta name="flags" content="ahem">
+<meta name="assert" content="White spaces are preserved, honoring the 'white-space: break-spaces', but the words are broken, honring the 'word-beak: break-all' even though there are previous breaking opportunities in the white-spaces.">
+<style>
+div {
+   position: relative;
+   font: 25px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 4ch;
+
+  white-space: break-spaces;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail"><span>X</span>XXX<br>X<span>X</span>XX<br>X<span>XXX</span><br><span>XXXX</span></div>
+  <div class="test"> XXXX XXX</div>
+</body>
diff --git a/css/css-text/white-space/break-spaces-007.html b/css/css-text/white-space/break-spaces-007.html
new file mode 100644
index 0000000..7721361
--- /dev/null
+++ b/css/css-text/white-space/break-spaces-007.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space - break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="match" href="reference/white-space-break-spaces-005-ref.html">
+<meta name="flags" content="ahem">
+<meta name="assert" content="A single leading white-space should be used, honoring white-space: break-spaces, to avoid overflow; however, a single preserved white-space at the end of the line cannot be wrapped, hence it hangs when breaking after it to move the rest of the text to the next line.">
+<style>
+div {
+   position: relative;
+   font: 25px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 4ch;
+
+  white-space: break-spaces;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail"><span>XXXX</span><br>XXXX<br>XXX<span>X</span><br><span>XXXX</span></div>
+  <div class="test"> XXXX XXX</div>
+</body>
diff --git a/css/css-text/white-space/break-spaces-008.html b/css/css-text/white-space/break-spaces-008.html
new file mode 100644
index 0000000..d183cac
--- /dev/null
+++ b/css/css-text/white-space/break-spaces-008.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space - break-spaces</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://www.w3.org/TR/css-text-3/#white-space-property">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="match" href="reference/white-space-break-spaces-005-ref.html">
+<meta name="flags" content="ahem">
+<meta name="assert" content="White spaces are preserved, honoring the 'white-space: break-spaces', which may lead to overfow. However, we can break before the first white-space after the word honoring the 'break-all' value.">
+<style>
+div {
+   position: relative;
+   font: 25px/1 Ahem;
+}
+.fail {
+  position: absolute;
+  color: red;
+  z-index: -1;
+}
+span { color: green; }
+.test {
+  color: green;
+  width: 4ch;
+
+  white-space: break-spaces;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="fail">XXXX<br><span>X</span>XX<span>X</span><br><span>XXXX</span><br><span>XXXX</span></div>
+  <div class="test">XXXX XX</div>
+</body>
diff --git a/css/css-text/white-space/pre-wrap-016.html b/css/css-text/white-space/pre-wrap-016.html
new file mode 100644
index 0000000..5f66a7b
--- /dev/null
+++ b/css/css-text/white-space/pre-wrap-016.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: white-space: pre-wrap</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-word">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/pre-wrap-001-ref.html">
+<meta name="assert" content="The word is not broken if there are previous breaking opportunities, honoring the white-space: pre-wrap value.">
+<style>
+div {
+   position: relative;
+   font-size: 20px;
+   font-family: Ahem;
+}
+.red {
+  position: absolute;
+  white-space: pre;
+  background: green;
+  color: red;
+  width: 40px;
+  height: 40px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  line-height: 1em;
+  width: 2ch;
+
+  white-space: pre-wrap;
+  word-break: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"><br>XX</div>
+  <div class="test"> XX</div>
+</body>
diff --git a/css/css-text/white-space/reference/white-space-break-spaces-005-ref.html b/css/css-text/white-space/reference/white-space-break-spaces-005-ref.html
new file mode 100644
index 0000000..3247ae5
--- /dev/null
+++ b/css/css-text/white-space/reference/white-space-break-spaces-005-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Reference File</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com" />
+<style>
+div {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+</style>
+<body>
+    <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div></div>
+</body>
diff --git a/css/css-text/word-break/word-break-break-all-010.html b/css/css-text/word-break/word-break-break-all-010.html
index 7d3bc05..be46d29 100644
--- a/css/css-text/word-break/word-break-break-all-010.html
+++ b/css/css-text/word-break/word-break-break-all-010.html
@@ -1,8 +1,9 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>CSS Text Test: overflow-wrap: break-word</title>
+<title>CSS Text Test: word-break: break-all</title>
 <link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
 <meta name="flags" content="ahem">
 <link rel="match" href="reference/word-break-break-all-010-ref.html">
 <meta name="assert" content="The word is broken even if pre-wrap provides a former breaking opportunity in leading white-space.">
diff --git a/css/css-text/word-break/word-break-break-all-011.html b/css/css-text/word-break/word-break-break-all-011.html
index 531c68d..fade439 100644
--- a/css/css-text/word-break/word-break-break-all-011.html
+++ b/css/css-text/word-break/word-break-break-all-011.html
@@ -1,8 +1,9 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>CSS Text Test: overflow-wrap: break-word</title>
+<title>CSS Text Test: word-break: break-all</title>
 <link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
 <meta name="flags" content="ahem">
 <link rel="match" href="reference/word-break-break-all-010-ref.html">
 <meta name="assert" content="A single leading white-space should account as soft breaking opportunity, honoring the 'white-space: pre-wrap', on top to the ones provided by 'word-break: break-all'.">
diff --git a/css/css-text/word-break/word-break-break-all-012.html b/css/css-text/word-break/word-break-break-all-012.html
new file mode 100644
index 0000000..cd3d440
--- /dev/null
+++ b/css/css-text/word-break/word-break-break-all-012.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: break-word</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/word-break-break-all-010-ref.html">
+<meta name="assert" content="The word is broken even if white-space: break-spaces provides a former breaking opportunity in leading white-space.">
+<style>
+div {
+   position: relative;
+   font-size: 20px;
+   font-family: Ahem;
+}
+.red {
+  position: absolute;
+  white-space: pre;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  line-height: 1em;
+  width: 5ch;
+
+  white-space: break-spaces;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"> XXXX<br>X</div>
+  <div class="test"> XXXXX</div>
+</body>
diff --git a/css/css-text/word-break/word-break-break-all-013.html b/css/css-text/word-break/word-break-break-all-013.html
new file mode 100644
index 0000000..85dce08
--- /dev/null
+++ b/css/css-text/word-break/word-break-break-all-013.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: word-break: break-all</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<meta name="flags" content="ahem">
+<link rel="match" href="reference/word-break-break-all-010-ref.html">
+<meta name="assert" content="A single leading white-space should account as soft breaking opportunity, honoring the 'white-space: break-spaces', on top to the ones provided by 'word-break: break-all'.">
+<style>
+div {
+   position: relative;
+   font-size: 20px;
+   font-family: Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  background: green;
+  line-height: 1em;
+  width: 1ch;
+  white-space: break-spaces;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">X<br>X<br>X</div>
+  <div class="test"> XX</div>
+</body>
diff --git a/css/css-values/attr-color-invalid-fallback.html b/css/css-values/attr-color-invalid-fallback.html
index 9225889..9e016d1 100644
--- a/css/css-values/attr-color-invalid-fallback.html
+++ b/css/css-values/attr-color-invalid-fallback.html
@@ -9,7 +9,7 @@
 	<meta name="assert" content="
 		When the fallback value of an attr() function is invalid, the delcaration is ignored.
 	" />
-
+	<meta name="flags" content="invalid">
 	<link
 		rel="author"
 		title="François REMY"
diff --git a/css/css-values/attr-invalid-type-001.html b/css/css-values/attr-invalid-type-001.html
index c427ade..1f88c37 100644
--- a/css/css-values/attr-invalid-type-001.html
+++ b/css/css-values/attr-invalid-type-001.html
@@ -9,7 +9,7 @@
 	<meta name="assert" content="
 		When the type of an attr() function is known and unexpected, the declaration is ignored
 	" />
-
+	<meta name="flags" content="invalid">
 	<link
 		rel="author"
 		title="François REMY"
diff --git a/css/css-values/attr-invalid-type-002.html b/css/css-values/attr-invalid-type-002.html
index 797700d..dbdac8a 100644
--- a/css/css-values/attr-invalid-type-002.html
+++ b/css/css-values/attr-invalid-type-002.html
@@ -9,7 +9,7 @@
 	<meta name="assert" content="
 		When the type of an attr() function is known and unexpected, the declaration is ignored
 	" />
-
+	<meta name="flags" content="invalid">
 	<link
 		rel="author"
 		title="François REMY"
diff --git a/css/css-values/attr-length-invalid-fallback.html b/css/css-values/attr-length-invalid-fallback.html
index 6a5d4df..e68fe7a 100644
--- a/css/css-values/attr-length-invalid-fallback.html
+++ b/css/css-values/attr-length-invalid-fallback.html
@@ -9,7 +9,7 @@
 	<meta name="assert" content="
 		When the attr() fallback is an invalid length, the delcaration is correctly ignored.
 	" />
-
+	<meta name="flags" content="invalid">
 	<link
 		rel="author"
 		title="François REMY"
diff --git a/css/css-values/attr-px-invalid-fallback.html b/css/css-values/attr-px-invalid-fallback.html
index 778697d..f8f88f5 100644
--- a/css/css-values/attr-px-invalid-fallback.html
+++ b/css/css-values/attr-px-invalid-fallback.html
@@ -9,7 +9,7 @@
 	<meta name="assert" content="
 		When the fallback of a pixel attribute reference is invalid, the declaration is ignored.
 	" />
-
+	<meta name="flags" content="invalid">
 	<link
 		rel="author"
 		title="François REMY"
diff --git a/encrypted-media/clearkey-check-encryption-scheme.https.html b/encrypted-media/clearkey-check-encryption-scheme.https.html
new file mode 100644
index 0000000..eb580ea
--- /dev/null
+++ b/encrypted-media/clearkey-check-encryption-scheme.https.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Encrypted Media Extensions: Check encryptionScheme with Clear Key</title>
+    <link rel="help" href="https://w3c.github.io/encrypted-media/">
+
+    <!-- Web Platform Test Harness scripts -->
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+
+    <!-- Helper scripts for Encrypted Media Extensions tests  -->
+    <script src=/encrypted-media/util/utils.js></script>
+    <script src=/encrypted-media/util/utf8.js></script>
+
+    <!-- Content metadata -->
+    <!--<script src=/encrypted-media/content/content-metadata.js></script>-->
+
+    <!-- The script for this specific test -->
+    <script src=/encrypted-media/scripts/check-encryption-scheme.js></script>
+
+  </head>
+  <body>
+    <div id='log'></div>
+
+    <script>
+        var config = {  keysystem: 'org.w3.clearkey' }
+
+        runTest(config);
+    </script>
+  </body>
+</html>
diff --git a/encrypted-media/clearkey-check-status-for-hdcp.https.html b/encrypted-media/clearkey-check-status-for-hdcp.https.html
new file mode 100644
index 0000000..5ec3b26
--- /dev/null
+++ b/encrypted-media/clearkey-check-status-for-hdcp.https.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Encrypted Media Extensions: Check HDCP status with Clear Key</title>
+    <link rel="help" href="https://w3c.github.io/encrypted-media/">
+
+    <!-- Web Platform Test Harness scripts -->
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+
+    <!-- Helper scripts for Encrypted Media Extensions tests  -->
+    <script src=/encrypted-media/util/utils.js></script>
+    <script src=/encrypted-media/util/utf8.js></script>
+
+    <!-- Content metadata -->
+    <!--<script src=/encrypted-media/content/content-metadata.js></script>-->
+
+    <!-- The script for this specific test -->
+    <script src=/encrypted-media/scripts/check-status-for-hdcp.js></script>
+
+  </head>
+  <body>
+    <div id='log'></div>
+
+    <script>
+        var config = {  keysystem: 'org.w3.clearkey' }
+
+        runTest(config);
+    </script>
+  </body>
+</html>
diff --git a/encrypted-media/drm-check-encryption-scheme.https.html b/encrypted-media/drm-check-encryption-scheme.https.html
new file mode 100644
index 0000000..862591b
--- /dev/null
+++ b/encrypted-media/drm-check-encryption-scheme.https.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Encrypted Media Extensions: Check encryptionScheme with DRM</title>
+    <link rel="help" href="https://w3c.github.io/encrypted-media/">
+
+    <!-- Web Platform Test Harness scripts -->
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+
+    <!-- Helper scripts for Encrypted Media Extensions tests  -->
+    <script src=/encrypted-media/util/utils.js></script>
+    <script src=/encrypted-media/util/utf8.js></script>
+
+    <!-- Content metadata -->
+    <!--<script src=/encrypted-media/content/content-metadata.js></script>-->
+
+    <!-- The script for this specific test -->
+    <script src=/encrypted-media/scripts/check-encryption-scheme.js></script>
+
+  </head>
+  <body>
+    <div id='log'></div>
+
+    <script>
+        var config = {  keysystem: getSupportedKeySystem() }
+
+        runTest(config);
+    </script>
+  </body>
+</html>
diff --git a/encrypted-media/drm-check-status-for-hdcp.https.html b/encrypted-media/drm-check-status-for-hdcp.https.html
new file mode 100644
index 0000000..68c45be
--- /dev/null
+++ b/encrypted-media/drm-check-status-for-hdcp.https.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Encrypted Media Extensions: Check HDCP status with DRM</title>
+    <link rel="help" href="https://w3c.github.io/encrypted-media/">
+
+    <!-- Web Platform Test Harness scripts -->
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+
+    <!-- Helper scripts for Encrypted Media Extensions tests  -->
+    <script src=/encrypted-media/util/utils.js></script>
+    <script src=/encrypted-media/util/utf8.js></script>
+
+    <!-- Content metadata -->
+    <!--<script src=/encrypted-media/content/content-metadata.js></script>-->
+
+    <!-- The script for this specific test -->
+    <script src=/encrypted-media/scripts/check-status-for-hdcp.js></script>
+
+  </head>
+  <body>
+    <div id='log'></div>
+
+    <script>
+        var config = {  keysystem: getSupportedKeySystem() }
+
+        runTest(config);
+    </script>
+  </body>
+</html>
diff --git a/encrypted-media/encrypted-media-supported-by-feature-policy.tentative.html b/encrypted-media/encrypted-media-supported-by-feature-policy.tentative.html
new file mode 100644
index 0000000..5b2d224
--- /dev/null
+++ b/encrypted-media/encrypted-media-supported-by-feature-policy.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that encrypted-media is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://github.com/w3c/encrypted-media/pull/432">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('encrypted-media', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise encrypted-media.');
+</script>
diff --git a/encrypted-media/scripts/check-encryption-scheme.js b/encrypted-media/scripts/check-encryption-scheme.js
new file mode 100644
index 0000000..5d62927
--- /dev/null
+++ b/encrypted-media/scripts/check-encryption-scheme.js
@@ -0,0 +1,41 @@
+function runTest(config, qualifier)
+{
+  function checkEncryptionScheme(encryptionScheme)
+  {
+    var simpleConfig = getSimpleConfiguration();
+    assert_greater_than(simpleConfig[0].audioCapabilities.length, 0);
+    simpleConfig[0].audioCapabilities.forEach(function(capability) {
+      capability.encryptionScheme = encryptionScheme;
+    });
+
+    return navigator.requestMediaKeySystemAccess(config.keysystem, simpleConfig)
+        .then(
+            function(access) {
+              var actualConfiguration = access.getConfiguration();
+              for (let i = 0; i < actualConfiguration.audioCapabilities.length; i++) {
+                const capability = actualConfiguration.audioCapabilities[i];
+
+                // If "encryptionScheme" is not supported, fail.
+                if (!('encryptionScheme' in capability)) {
+                  return Promise.reject('Not implemented');
+                }
+
+                // If "encryptionScheme" is supported, it should be returned.
+                assert_equals(capability.encryptionScheme, encryptionScheme);
+              }
+              return Promise.resolve('Supported');
+            },
+            function error() {
+              // CDM does not support "encryptionScheme". Test should still pass.
+              return Promise.resolve('Not supported');
+            });
+  }
+
+  promise_test(
+      () => checkEncryptionScheme('cenc'),
+      testnamePrefix(qualifier, config.keysystem) + ' support for "cenc" encryption scheme.');
+
+  promise_test(
+      () => checkEncryptionScheme('cbcs'),
+      testnamePrefix(qualifier, config.keysystem) + ' support for "cbcs" encryption scheme.');
+}
diff --git a/encrypted-media/scripts/check-status-for-hdcp.js b/encrypted-media/scripts/check-status-for-hdcp.js
new file mode 100644
index 0000000..ac30819
--- /dev/null
+++ b/encrypted-media/scripts/check-status-for-hdcp.js
@@ -0,0 +1,26 @@
+function runTest(config, qualifier)
+{
+  function checkStatusForMinHdcpVersionPolicy(hdcpVersion)
+  {
+    return navigator.requestMediaKeySystemAccess(config.keysystem, getSimpleConfiguration())
+        .then(function(access) {
+          return access.createMediaKeys();
+        })
+        .then(function(mediaKeys) {
+          // As HDCP policy depends on the hardware running this test,
+          // don't bother checking the result returned as it may or
+          // may not be supported. This simply verifies that
+          // getStatusForPolicy() exists and doesn't blow up.
+          return mediaKeys.getStatusForPolicy({minHdcpVersion: hdcpVersion});
+        });
+  }
+
+  promise_test(
+      () => checkStatusForMinHdcpVersionPolicy(''),
+      testnamePrefix(qualifier, config.keysystem) +
+          ' support for empty HDCP version.');
+
+  promise_test(
+      () => checkStatusForMinHdcpVersionPolicy('1.0'),
+      testnamePrefix(qualifier, config.keysystem) + ' support for HDCP 1.0.');
+}
diff --git a/event-timing/supported-types.window.js b/event-timing/supported-types.window.js
new file mode 100644
index 0000000..c81d501
--- /dev/null
+++ b/event-timing/supported-types.window.js
@@ -0,0 +1,11 @@
+test(() => {
+  if (typeof PerformanceObserver.supportedEntryTypes === "undefined")
+    assert_unreached("supportedEntryTypes is not supported.");
+  const types = PerformanceObserver.supportedEntryTypes;
+  assert_true(types.includes("firstInput"),
+    "There should be 'firstInput' in PerformanceObserver.supportedEntryTypes");
+  assert_true(types.includes("event"),
+    "There should be 'event' in PerformanceObserver.supportedEntryTypes");
+  assert_greater_than(types.indexOf("firstInput"), types.indexOf('event'),
+    "The 'firstInput' entry should appear after the 'event' entry");
+}, "supportedEntryTypes contains 'event' and 'firstInput'.");
diff --git a/feature-policy/payment-supported-by-feature-policy.tentative.html b/feature-policy/payment-supported-by-feature-policy.tentative.html
new file mode 100644
index 0000000..07dec70
--- /dev/null
+++ b/feature-policy/payment-supported-by-feature-policy.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that payment is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://github.com/w3c/payment-request/issues/600">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('payment', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise payment.');
+</script>
diff --git a/feature-policy/picture-in-picture-supported-by-feature-policy.html b/feature-policy/picture-in-picture-supported-by-feature-policy.html
new file mode 100644
index 0000000..a65c682
--- /dev/null
+++ b/feature-policy/picture-in-picture-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that picture-in-picture is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://wicg.github.io/picture-in-picture/#feature-policy">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('picture-in-picture', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise picture-in-picture.');
+</script>
diff --git a/feature-policy/reporting/unoptimized-image.jpg b/feature-policy/reporting/unoptimized-image.jpg
new file mode 100644
index 0000000..599137a
--- /dev/null
+++ b/feature-policy/reporting/unoptimized-image.jpg
Binary files differ
diff --git a/feature-policy/reporting/unoptimized-images-reporting.html b/feature-policy/reporting/unoptimized-images-reporting.html
new file mode 100644
index 0000000..fb27a13
--- /dev/null
+++ b/feature-policy/reporting/unoptimized-images-reporting.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <img src="./unoptimized-image.jpg">
+    <script>
+var check_report_format = (reports, observer) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy-violation");
+  assert_equals(report.url, document.location.href);
+  assert_equals(report.body.featureId, "unoptimized-images");
+  assert_equals(report.body.disposition, "enforce");
+};
+
+async_test(t => {
+  new ReportingObserver(t.step_func_done(check_report_format),
+                        {types: ['feature-policy-violation'], buffered: true}).observe();
+}, "unoptimized-images Report Format");
+    </script>
+  </body>
+</html>
diff --git a/feature-policy/reporting/unoptimized-images-reporting.html.headers b/feature-policy/reporting/unoptimized-images-reporting.html.headers
new file mode 100644
index 0000000..10b4123
--- /dev/null
+++ b/feature-policy/reporting/unoptimized-images-reporting.html.headers
@@ -0,0 +1 @@
+Feature-Policy: unoptimized-images 'none'
diff --git a/fetch/cross-origin-resource-policy/syntax.any.js b/fetch/cross-origin-resource-policy/syntax.any.js
index cf5b06d..dc87497 100644
--- a/fetch/cross-origin-resource-policy/syntax.any.js
+++ b/fetch/cross-origin-resource-policy/syntax.any.js
@@ -8,7 +8,8 @@
   "SAME-ORIGIN",
   "Same-Origin",
   "same-origin, <>",
-  "same-origin, same-origin"
+  "same-origin, same-origin",
+  "https://www.example.com",  // See https://github.com/whatwg/fetch/issues/760
 ].forEach(incorrectHeaderValue => {
   // Note: an incorrect value results in a successful load, so this test is only meaningful in
   // implementations with support for the header.
diff --git a/geolocation-sensor/GeolocationSensor-supported-by-feature-policy.html b/geolocation-sensor/GeolocationSensor-supported-by-feature-policy.html
new file mode 100644
index 0000000..aa05f33
--- /dev/null
+++ b/geolocation-sensor/GeolocationSensor-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that geolocation is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/sensors/#feature-policy-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('geolocation', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise geolocation.');
+</script>
diff --git a/gyroscope/Gyroscope-supported-by-feature-policy.html b/gyroscope/Gyroscope-supported-by-feature-policy.html
new file mode 100644
index 0000000..06a4137
--- /dev/null
+++ b/gyroscope/Gyroscope-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that gyroscope is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/sensors/#feature-policy-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('gyroscope', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise gyroscope.');
+</script>
diff --git a/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
index caac56a..888343b 100644
--- a/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -106,16 +106,22 @@
       C.location[prop]; // Shouldn't throw.
       Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw.
       assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOwnProperty for " + prop);
+      assert_throws("SecurityError", function() { C.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
+    }
+    else if (prop == 'href') {
+      Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw.
+      assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOwnProperty for " + prop);
+      assert_throws("SecurityError", function() { C.location[prop] },
+                    "Should throw reading href on Location");
     }
     else {
-      assert_throws("SecurityError", function() { C[prop]; }, "Should throw when accessing " + prop + " on Location");
-      assert_throws("SecurityError", function() { Object.getOwnPropertyDescriptor(C, prop); },
+      assert_throws("SecurityError", function() { C.location[prop]; }, "Should throw when accessing " + prop + " on Location");
+      assert_throws("SecurityError", function() { Object.getOwnPropertyDescriptor(C.location, prop); },
                     "Should throw when accessing property descriptor for " + prop + " on Location");
-      assert_throws("SecurityError", function() { Object.prototype.hasOwnProperty.call(C, prop); },
+      assert_throws("SecurityError", function() { Object.prototype.hasOwnProperty.call(C.location, prop); },
                     "Should throw when invoking hasOwnProperty for " + prop + " on Location");
+      assert_throws("SecurityError", function() { C.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
     }
-    if (prop != 'href')
-      assert_throws("SecurityError", function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
   }
 }, "Only whitelisted properties are accessible cross-origin");
 
@@ -150,11 +156,22 @@
     assert_throws(new TypeError, function() { protoSetter.call(C, new Object()); }, "proto setter |call| on cross-origin Window");
     assert_throws(new TypeError, function() { protoSetter.call(C.location, new Object()); }, "proto setter |call| on cross-origin Location");
   });
+  // Hack to avoid "duplicate test name" harness issues.
+  setters.forEach(function(protoSetter) {
+    test(function() { protoSetter.call(C, null); },
+         "proto setter |call| on cross-origin Window with null (" + protoSetter + ")");
+    test(function() { protoSetter.call(C.location, null); },
+         "proto setter |call| on cross-origin Location with null (" + protoSetter + ")");
+  });
   if (Reflect.setPrototypeOf) {
     assert_false(Reflect.setPrototypeOf(C, new Object()),
                  "Reflect.setPrototypeOf on cross-origin Window");
+    assert_true(Reflect.setPrototypeOf(C, null),
+                "Reflect.setPrototypeOf on cross-origin Window with null");
     assert_false(Reflect.setPrototypeOf(C.location, new Object()),
                 "Reflect.setPrototypeOf on cross-origin Location");
+    assert_true(Reflect.setPrototypeOf(C.location, null),
+                "Reflect.setPrototypeOf on cross-origin Location with null");
   }
 }, "[[SetPrototypeOf]] should return false");
 
@@ -230,6 +247,19 @@
   assert_equals(typeof D.then, "object");
 }, "[[GetOwnProperty]] - Subframe named 'then' should shadow the default 'then' value");
 
+addTest(function() {
+  assert_equals(typeof D.close, "function");
+  assert_equals(typeof D.open, "object");
+}, "[[GetOwnProperty]] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties");
+
+addTest(function() {
+  assert_equals(typeof Object.getOwnPropertyDescriptor(C, '0').value, "object");
+  assert_equals(typeof Object.getOwnPropertyDescriptor(C, '1').value, "object");
+  assert_throws("SecurityError", function() {
+    Object.getOwnPropertyDescriptor(C, '2');
+  });
+}, "[[GetOwnProperty]] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window.");
+
 /*
  * [[Delete]]
  */
diff --git a/html/browsers/origin/cross-origin-objects/frame-with-then.html b/html/browsers/origin/cross-origin-objects/frame-with-then.html
index 96cdf1e..1127995 100644
--- a/html/browsers/origin/cross-origin-objects/frame-with-then.html
+++ b/html/browsers/origin/cross-origin-objects/frame-with-then.html
@@ -6,5 +6,9 @@
     <!-- A subframe to test "then" behavior -->
     <iframe name="then"></iframe>
     <iframe name="b"></iframe>
+    <!-- Two subframes with names corresponding to IDL-defined properties; one
+         a cross-origin-exposed property and one not exposed cross-origin -->
+    <iframe name="close"></iframe>
+    <iframe name="open"></iframe>
   </body>
 </html>
diff --git a/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html b/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html
new file mode 100644
index 0000000..af4de6b
--- /dev/null
+++ b/html/semantics/embedded-content/media-elements/autoplay-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that autoplay is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/infrastructure.html#policy-controlled-features">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('autoplay', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise autoplay.');
+</script>
diff --git a/interfaces/IndexedDB.idl b/interfaces/IndexedDB.idl
index 137528c..868338b 100644
--- a/interfaces/IndexedDB.idl
+++ b/interfaces/IndexedDB.idl
@@ -200,6 +200,7 @@
   readonly attribute DOMException error;
 
   IDBObjectStore objectStore(DOMString name);
+  void commit();
   void abort();
 
   // Event handlers:
diff --git a/interfaces/mediastream-recording.idl b/interfaces/mediastream-recording.idl
index 5bf661e..0be0538 100644
--- a/interfaces/mediastream-recording.idl
+++ b/interfaces/mediastream-recording.idl
@@ -18,7 +18,7 @@
   readonly attribute unsigned long videoBitsPerSecond;
   readonly attribute unsigned long audioBitsPerSecond;
 
-  void start(optional long timeslice);
+  void start(optional unsigned long timeslice);
   void stop();
   void pause();
   void resume();
diff --git a/magnetometer/Magnetometer-supported-by-feature-policy.html b/magnetometer/Magnetometer-supported-by-feature-policy.html
new file mode 100644
index 0000000..a049363
--- /dev/null
+++ b/magnetometer/Magnetometer-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that magnetometer is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/sensors/#feature-policy-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('magnetometer', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise magnetometer.');
+</script>
diff --git a/mediacapture-streams/MediaStream-supported-by-feature-policy.html b/mediacapture-streams/MediaStream-supported-by-feature-policy.html
new file mode 100644
index 0000000..ef29ae6
--- /dev/null
+++ b/mediacapture-streams/MediaStream-supported-by-feature-policy.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Test that accelerometer is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/mediacapture-main/#feature-policy-integration">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('camera', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise camera.');
+
+test(() => {
+    assert_in_array('microphone', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise microphone.');
+</script>
diff --git a/mediacapture-streams/MediaStreamTrack-getSettings.https.html b/mediacapture-streams/MediaStreamTrack-getSettings.https.html
index 158c901..c062205 100644
--- a/mediacapture-streams/MediaStreamTrack-getSettings.https.html
+++ b/mediacapture-streams/MediaStreamTrack-getSettings.https.html
@@ -1,6 +1,7 @@
 <!doctype html>
 <title>MediaStreamTrack GetSettings</title>
 <p class="instructions">When prompted, accept to share your video stream.</p>
+<meta name=timeout content=long>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script>
diff --git a/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html b/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html
index edde533..45f3f0a 100644
--- a/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html
+++ b/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html
@@ -68,7 +68,6 @@
     <button onclick="
     const expectedAddress = {
       country: 'AU',
-      regionCode: 'QLD',
       addressLine: '55 test st',
       city: 'Chapel Hill',
       dependentLocality: '',
diff --git a/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html b/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
index 356f30d..c3e2ba7 100644
--- a/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
+++ b/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
@@ -82,11 +82,6 @@
     </button>
   </li>
   <li>
-    <button onclick="retryShowsShippingAddressMember(this, { regionCode: 'REGIONCODE ERROR' });">
-      The payment sheet shows "REGIONCODE ERROR" for the shipping address' region code.
-    </button>
-  </li>
-  <li>
     <button onclick="retryShowsShippingAddressMember(this, { sortingCode: 'SORTINGCODE ERROR' });">
       The payment sheet shows "SORTINGCODE ERROR" for the shipping address' sorting code.
     </button>
diff --git a/payment-request/historical.https.html b/payment-request/historical.https.html
index cbd29d4..e681f64 100644
--- a/payment-request/historical.https.html
+++ b/payment-request/historical.https.html
@@ -21,6 +21,9 @@
 
   // https://github.com/w3c/payment-request/pull/765
   ["languageCode", "PaymentAddress"],
+
+  // https://github.com/w3c/payment-request/pull/823
+  ["regionCode", "PaymentAddress"],
 ].forEach(([member, interf]) => {
   test(() => {
     assert_false(member in window[interf].prototype);
diff --git a/quirks/unitless-length/excluded-properties-001.html b/quirks/unitless-length/excluded-properties-001.html
index 2e33cec..09fedcf 100644
--- a/quirks/unitless-length/excluded-properties-001.html
+++ b/quirks/unitless-length/excluded-properties-001.html
@@ -30,6 +30,13 @@
     'grid-template-columns',
     'grid-template-rows',
     'inline-size',
+    'inset',
+    'inset-block',
+    'inset-block-end',
+    'inset-block-start',
+    'inset-inline',
+    'inset-inline-end',
+    'inset-inline-start',
     'margin-block-end',
     'margin-block-start',
     'margin-inline-end',
diff --git a/quirks/unitless-length/excluded-properties-003.html b/quirks/unitless-length/excluded-properties-003.html
new file mode 100644
index 0000000..4cbe012
--- /dev/null
+++ b/quirks/unitless-length/excluded-properties-003.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+<meta charset="utf-8">
+<title>inset does not support quirky-length</title>
+<link rel="help" href="https://quirks.spec.whatwg.org/#the-unitless-length-quirk">
+<meta name="assert" content="quirky-length is not supported by inset.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+'use strict';
+
+test(() => {
+  const quirky_values = [
+    '1',
+    '1 2px',
+    '1px 2',
+    '1 2',
+    '1 2px 3px',
+    '1px 2 3px',
+    '1px 2px 3',
+    '1 2 3',
+    '1 2px 3px 4px',
+    '1px 2 3px 4px',
+    '1px 2px 3 4px',
+    '1px 2px 3px 4',
+    '1 2 3 4'
+  ];
+
+  target.style['inset'] = '5px 6px 7px 8px';
+
+  for (let value of quirky_values) {
+    target.style['inset'] = value;
+    assert_equals(target.style['inset'], '5px 6px 7px 8px', 'inset rejects quirky length "' + value + '"');
+  }
+}, 'inset does not support quirky length values');
+
+</script>
+</body>
+</html>
diff --git a/resources/testharness.js b/resources/testharness.js
index ed3ffb4..fb86c58 100644
--- a/resources/testharness.js
+++ b/resources/testharness.js
@@ -2135,6 +2135,9 @@
                     }
                 } else if (p == "timeout_multiplier") {
                     this.timeout_multiplier = value;
+                    if (this.timeout_length) {
+                         this.timeout_length *= this.timeout_multiplier;
+                    }
                 }
             }
         }
diff --git a/service-workers/service-worker/appcache-ordering-main.https.html b/service-workers/service-worker/appcache-ordering-main.https.html
index 921dae0..a86671c 100644
--- a/service-workers/service-worker/appcache-ordering-main.https.html
+++ b/service-workers/service-worker/appcache-ordering-main.https.html
@@ -5,7 +5,6 @@
 <body>
 <script>
 
-var INSTALL_APPCACHE_URL = "resources/appcache-ordering.install.html";
 var IS_APPCACHED_URL = "resources/appcache-ordering.is-appcached.html";
 var SERVICE_WORKER_SCOPE = "resources/appcache-ordering";
 var SERVICE_WORKER_SCRIPT = "resources/empty-worker.js";
@@ -15,31 +14,6 @@
 
 var frames = [];
 
-// Called by the INSTALL_APPCACHE_URL child frame.
-function notify_appcache_installed(success) {
-  if (success)
-    resolve_install_appcache();
-  else
-    reject_install_appcache();
-}
-
-function install_appcache() {
-  return new Promise(function(resolve, reject) {
-      var frame = document.createElement('iframe');
-      frames.push(frame);
-      frame.src = INSTALL_APPCACHE_URL;
-      document.body.appendChild(frame);
-      resolve_install_appcache = function() {
-          document.body.removeChild(frame);
-          resolve();
-        };
-      reject_install_appcache = function() {
-          document.body.removeChild(frame);
-          reject();
-        };
-  });
-}
-
 var resolve_is_appcached = undefined;
 
 // Called by the IS_APPCACHED_URL child frame.
@@ -63,7 +37,7 @@
 promise_test(function(t) {
     return service_worker_unregister(t, SERVICE_WORKER_SCOPE)
       .then(function() {
-          return install_appcache();
+          return install_appcache_ordering_manifest();
         })
       .then(function() {
           return is_appcached();
diff --git a/service-workers/service-worker/claim-fetch-with-appcache.https.html b/service-workers/service-worker/claim-fetch-with-appcache.https.html
new file mode 100644
index 0000000..4890a84
--- /dev/null
+++ b/service-workers/service-worker/claim-fetch-with-appcache.https.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<body>
+<script>
+
+// This test makes a frame controlled by AppCache, then registers a service
+// worker that calls claim() to control the frame. AppCache should be completely
+// bypassed once the service worker claims the frame.
+
+const fetch_text = async frame => {
+  const response = await
+    frame.contentWindow.fetch('appcache-ordering.is-appcached.js');
+  return await response.text();
+};
+
+const check_is_appcached = async frame => {
+  // This should FALLBACK to ordering.is_appcached.js as in the manifest
+  // if the appcache is effective.
+  const response = await
+    frame.contentWindow.fetch('appcache-ordering.is-appcached404.js');
+  return await response.ok;
+};
+
+promise_test(async t => {
+  const scope = 'resources/';
+  const script = 'resources/claim-worker.js';
+
+  await install_appcache_ordering_manifest();
+
+  // Create the test iframe.
+  const frame = await with_iframe('resources/blank.html');
+  t.add_cleanup(async () => {
+    if (frame) frame.remove();
+    return service_worker_unregister(t, scope);
+  });
+
+  // Check that the appcache controls the frame.
+  assert_equals(await check_is_appcached(frame), true,
+                'AppCache should be present');
+
+  // Check the controller and test with fetch.
+  assert_equals(frame.contentWindow.navigator.controller, undefined,
+                'Should have no controller.');
+  assert_equals(await fetch_text(frame), 'var is_appcached = true;\n',
+                'fetch() should not be intercepted.');
+
+  // Register a service worker.
+  let registration = await service_worker_unregister_and_register(t, script, scope);
+  const worker = registration.installing;
+  await wait_for_state(t, worker, 'activated');
+
+  // Let the service worker claim the iframe.
+  const channel = new MessageChannel();
+  const check_message = new Promise(resolve => {
+    channel.port1.onmessage = async e => {
+      assert_equals(e.data, 'PASS', 'Worker call to claim() should fulfill.');
+      resolve();
+    };
+  });
+  worker.postMessage({port: channel.port2}, [channel.port2]);
+  await check_message;
+
+  // Check that the appcache does NOT control the frame.
+  assert_equals(await check_is_appcached(frame), false,
+                'AppCache should not be present');
+
+  // Check the controller and test with fetch.
+  registration = await
+    frame.contentWindow.navigator.serviceWorker.getRegistration(scope);
+  assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
+                registration.active, 'iframe should be claimed.');
+  assert_equals(await fetch_text(frame), 'Intercepted!',
+                'fetch() should be intercepted.');
+}, 'fetch() should be intercepted after the client is claimed.')
+
+</script>
+</body>
diff --git a/service-workers/service-worker/resources/appcache-ordering.manifest b/service-workers/service-worker/resources/appcache-ordering.manifest
index 0deed0e..e6597cc 100644
--- a/service-workers/service-worker/resources/appcache-ordering.manifest
+++ b/service-workers/service-worker/resources/appcache-ordering.manifest
@@ -1,6 +1,7 @@
 CACHE MANIFEST
 
 appcache-ordering.is-appcached.html
+blank.html
 
 FALLBACK:
 appcache-ordering.is-appcached404.js appcache-ordering.is-appcached.js
diff --git a/service-workers/service-worker/resources/claim-worker.js b/service-workers/service-worker/resources/claim-worker.js
index 53f210c..1800407 100644
--- a/service-workers/service-worker/resources/claim-worker.js
+++ b/service-workers/service-worker/resources/claim-worker.js
@@ -14,5 +14,6 @@
   });
 
 self.addEventListener('fetch', function(event) {
-    event.respondWith(new Response('Intercepted!'));
-});
+    if (!/404/.test(event.request.url))
+      event.respondWith(new Response('Intercepted!'));
+  });
diff --git a/service-workers/service-worker/resources/test-helpers.sub.js b/service-workers/service-worker/resources/test-helpers.sub.js
index 7efde35..1410131 100644
--- a/service-workers/service-worker/resources/test-helpers.sub.js
+++ b/service-workers/service-worker/resources/test-helpers.sub.js
@@ -276,3 +276,33 @@
   await wait_for_state(t, registration.installing, 'activated');
   await registration.unregister();
 }
+
+// This installs resources/appcache-ordering.manifest.
+function install_appcache_ordering_manifest() {
+  let resolve_install_appcache;
+  let reject_install_appcache;
+
+  // This is notified by the child iframe, i.e. appcache-ordering.install.html,
+  // that's to be created below.
+  window.notify_appcache_installed = success => {
+    if (success)
+      resolve_install_appcache();
+    else
+      reject_install_appcache();
+  };
+
+  return new Promise((resolve, reject) => {
+      const frame = document.createElement('iframe');
+      frame.src = 'resources/appcache-ordering.install.html';
+      document.body.appendChild(frame);
+      resolve_install_appcache = function() {
+          document.body.removeChild(frame);
+          resolve();
+        };
+      reject_install_appcache = function() {
+          document.body.removeChild(frame);
+          reject();
+        };
+  });
+}
+
diff --git a/signed-exchange/resources/sxg-util.js b/signed-exchange/resources/sxg-util.js
index 0d0e263..7d592ea 100644
--- a/signed-exchange/resources/sxg-util.js
+++ b/signed-exchange/resources/sxg-util.js
@@ -41,3 +41,27 @@
 function innerURLOrigin() {
   return 'https://127.0.0.1:8444';
 }
+
+function runReferrerTests(test_cases) {
+  for (const i in test_cases) {
+    const test_case = test_cases[i];
+    promise_test(async (t) => {
+      const sxgUrl = test_case.origin + '/signed-exchange/resources/sxg/' +
+                     test_case.sxg;
+      const message =
+          await openSXGInIframeAndWaitForMessage(
+              t, sxgUrl, test_case.referrerPolicy);
+      assert_false(message.is_fallback);
+      assert_equals(message.referrer, test_case.expectedReferrer);
+
+      const invalidSxgUrl =
+          test_case.origin + '/signed-exchange/resources/sxg/invalid-' +
+          test_case.sxg;
+      const fallbackMessage =
+          await openSXGInIframeAndWaitForMessage(
+                t, invalidSxgUrl, test_case.referrerPolicy);
+      assert_true(fallbackMessage.is_fallback);
+      assert_equals(fallbackMessage.referrer, test_case.expectedReferrer);
+    }, 'Referrer of SignedHTTPExchange test : ' + JSON.stringify(test_case));
+  }
+}
diff --git a/signed-exchange/sxg-referrer-policy-header.tentative.https.html b/signed-exchange/sxg-referrer-policy-header.tentative.https.html
new file mode 100644
index 0000000..5dff6af
--- /dev/null
+++ b/signed-exchange/sxg-referrer-policy-header.tentative.https.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Referrer-Policy header in outer SXG response</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+(() => {
+  const SAME_ORIGIN = get_host_info().HTTPS_ORIGIN;
+  const SAME_ORIGIN_SXG = 'sxg-referrer-same-origin.sxg';
+  const TEST_CASES = [
+    // Referrer-Policy header in outer SXG response.
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,no-referrer)',
+      referrerPolicy: undefined,
+      expectedReferrer: ''
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG +
+           '?pipe=header(Referrer-Policy,no-referrer-when-downgrade)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,origin)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,same-origin)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,strict-origin)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG +
+           '?pipe=header(Referrer-Policy,strict-origin-when-cross-origin)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,unsafe-url)',
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+  ];
+  runReferrerTests(TEST_CASES);
+})();
+</script>
+</body>
diff --git a/signed-exchange/sxg-referrer-remote-physical-remote-logical.tentative.https.html b/signed-exchange/sxg-referrer-remote-physical-remote-logical.tentative.https.html
new file mode 100644
index 0000000..c2870fd
--- /dev/null
+++ b/signed-exchange/sxg-referrer-remote-physical-remote-logical.tentative.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Referrer of SignedHTTPExchange(physical:remote origin, logical:remote origin)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+(() => {
+  const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+  const REMOTE_ORIGIN_SXG = 'sxg-referrer-remote-origin.sxg';
+  const TEST_CASES = [
+    // Physical origin = remote origin. Logical origin = remote origin.
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer',
+      expectedReferrer: ''
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer-when-downgrade',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'same-origin',
+      expectedReferrer: ''
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin-when-cross-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'unsafe-url',
+      expectedReferrer: document.location.href
+    },
+  ];
+  runReferrerTests(TEST_CASES);
+})();
+</script>
+</body>
diff --git a/signed-exchange/sxg-referrer-remote-physical-same-logical.tentative.https.html b/signed-exchange/sxg-referrer-remote-physical-same-logical.tentative.https.html
new file mode 100644
index 0000000..b3c77d1
--- /dev/null
+++ b/signed-exchange/sxg-referrer-remote-physical-same-logical.tentative.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Referrer of SignedHTTPExchange(physical:remote origin, logical:same origin)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+(() => {
+  const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+  const SAME_ORIGIN_SXG = 'sxg-referrer-same-origin.sxg';
+  const TEST_CASES = [
+    // Physical origin = remote origin. Logical origin = same origin.
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer',
+      expectedReferrer: ''
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer-when-downgrade',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'same-origin',
+      expectedReferrer: ''
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin-when-cross-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: REMOTE_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'unsafe-url',
+      expectedReferrer: document.location.href
+    },
+  ];
+  runReferrerTests(TEST_CASES);
+})();
+</script>
+</body>
diff --git a/signed-exchange/sxg-referrer-same-physical-remote-logical.tentative.https.html b/signed-exchange/sxg-referrer-same-physical-remote-logical.tentative.https.html
new file mode 100644
index 0000000..8a6cdd9
--- /dev/null
+++ b/signed-exchange/sxg-referrer-same-physical-remote-logical.tentative.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Referrer of SignedHTTPExchange(physical:same origin, logical:remote origin)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+(() => {
+  const SAME_ORIGIN = get_host_info().HTTPS_ORIGIN;
+  const REMOTE_ORIGIN_SXG = 'sxg-referrer-remote-origin.sxg';
+  const TEST_CASES = [
+    // Physical origin = same origin. Logical origin = remote origin.
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer',
+      expectedReferrer: ''
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer-when-downgrade',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'same-origin',
+      expectedReferrer: ''
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin-when-cross-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: REMOTE_ORIGIN_SXG,
+      referrerPolicy: 'unsafe-url',
+      expectedReferrer: document.location.href
+    },
+  ];
+  runReferrerTests(TEST_CASES);
+})();
+</script>
+</body>
diff --git a/signed-exchange/sxg-referrer-same-physical-same-logical.tentative.https.html b/signed-exchange/sxg-referrer-same-physical-same-logical.tentative.https.html
new file mode 100644
index 0000000..8891716
--- /dev/null
+++ b/signed-exchange/sxg-referrer-same-physical-same-logical.tentative.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Referrer of SignedHTTPExchange(physical:same origin, logical:same origin)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+(() => {
+  const SAME_ORIGIN = get_host_info().HTTPS_ORIGIN;
+  const SAME_ORIGIN_SXG = 'sxg-referrer-same-origin.sxg';
+  const TEST_CASES = [
+    // Physical origin = same origin. Logical origin = same origin.
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: undefined,
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer',
+      expectedReferrer: ''
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'no-referrer-when-downgrade',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'same-origin',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin',
+      expectedReferrer: document.location.origin + '/'
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'strict-origin-when-cross-origin',
+      expectedReferrer: document.location.href
+    },
+    {
+      origin: SAME_ORIGIN,
+      sxg: SAME_ORIGIN_SXG,
+      referrerPolicy: 'unsafe-url',
+      expectedReferrer: document.location.href
+    },
+  ];
+  runReferrerTests(TEST_CASES);
+})();
+</script>
+</body>
diff --git a/signed-exchange/sxg-referrer.tentative.https.html b/signed-exchange/sxg-referrer.tentative.https.html
deleted file mode 100644
index 3be9b1f..0000000
--- a/signed-exchange/sxg-referrer.tentative.https.html
+++ /dev/null
@@ -1,284 +0,0 @@
-<!DOCTYPE html>
-<title>Referrer of SignedHTTPExchange</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-<script src="./resources/sxg-util.js"></script>
-<body>
-<script>
-(() => {
-  const SAME_ORIGIN = get_host_info().HTTPS_ORIGIN;
-  const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
-  const SAME_ORIGIN_SXG = 'sxg-referrer-same-origin.sxg';
-  const REMOTE_ORIGIN_SXG = 'sxg-referrer-remote-origin.sxg';
-  const TEST_CASES = [
-    // Physical origin = same origin. Logical origin = same origin.
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer',
-      expectedReferrer: ''
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer-when-downgrade',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'same-origin',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin-when-cross-origin',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'unsafe-url',
-      expectedReferrer: document.location.href
-    },
-
-    // Physical origin = same origin. Logical origin = remote origin.
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer',
-      expectedReferrer: ''
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer-when-downgrade',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'same-origin',
-      expectedReferrer: ''
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin-when-cross-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'unsafe-url',
-      expectedReferrer: document.location.href
-    },
-
-    // Physical origin = remote origin. Logical origin = same origin.
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer',
-      expectedReferrer: ''
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer-when-downgrade',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'same-origin',
-      expectedReferrer: ''
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin-when-cross-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: SAME_ORIGIN_SXG,
-      referrerPolicy: 'unsafe-url',
-      expectedReferrer: document.location.href
-    },
-
-    // Physical origin = remote origin. Logical origin = remote origin.
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer',
-      expectedReferrer: ''
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'no-referrer-when-downgrade',
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'same-origin',
-      expectedReferrer: ''
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'strict-origin-when-cross-origin',
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: REMOTE_ORIGIN,
-      sxg: REMOTE_ORIGIN_SXG,
-      referrerPolicy: 'unsafe-url',
-      expectedReferrer: document.location.href
-    },
-
-    // Referrer-Policy header in outer SXG response.
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,no-referrer)',
-      referrerPolicy: undefined,
-      expectedReferrer: ''
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG +
-           '?pipe=header(Referrer-Policy,no-referrer-when-downgrade)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,origin)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,same-origin)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,strict-origin)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.origin + '/'
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG +
-           '?pipe=header(Referrer-Policy,strict-origin-when-cross-origin)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-    {
-      origin: SAME_ORIGIN,
-      sxg: SAME_ORIGIN_SXG + '?pipe=header(Referrer-Policy,unsafe-url)',
-      referrerPolicy: undefined,
-      expectedReferrer: document.location.href
-    },
-  ];
-  for (const i in TEST_CASES) {
-    const test_case = TEST_CASES[i];
-    promise_test(async (t) => {
-      const sxgUrl = test_case.origin + '/signed-exchange/resources/sxg/' +
-                     test_case.sxg;
-      const message =
-          await openSXGInIframeAndWaitForMessage(
-              t, sxgUrl, test_case.referrerPolicy);
-      assert_false(message.is_fallback);
-      assert_equals(message.referrer, test_case.expectedReferrer);
-
-      const invalidSxgUrl =
-          test_case.origin + '/signed-exchange/resources/sxg/invalid-' +
-          test_case.sxg;
-      const fallbackMessage =
-          await openSXGInIframeAndWaitForMessage(
-                t, invalidSxgUrl, test_case.referrerPolicy);
-      assert_true(fallbackMessage.is_fallback);
-      assert_equals(fallbackMessage.referrer, test_case.expectedReferrer);
-    }, 'Referrer of SignedHTTPExchange test : ' + JSON.stringify(test_case));
-  }
-})();
-</script>
-</body>
diff --git a/storage/estimate-usage-details-application-cache.https.tentative.html b/storage/estimate-usage-details-application-cache.https.tentative.html
new file mode 100644
index 0000000..464a998
--- /dev/null
+++ b/storage/estimate-usage-details-application-cache.https.tentative.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+<title>Quota Estimate: usage details reflect application cache changes.</title>
+<meta charset='utf-8'>
+<link rel='author' href='jarrydg@chromium.org' title='Jarryd Goodman'>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='../cookie-store/resources/helpers.js'></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+  let estimate = await navigator.storage.estimate();
+
+  const usageBeforeCreate = estimate.usageDetails.applicationCache || 0;
+
+  const iframe = await
+      createIframe('./resources/iframe_with_appcache_manifest.html', t);
+  await waitForMessage();
+
+  estimate = await navigator.storage.estimate();
+  assert_true('applicationCache' in estimate.usageDetails);
+  const usageAfterCreate = estimate.usageDetails.applicationCache;
+
+  assert_greater_than(
+      usageAfterCreate, usageBeforeCreate);
+}, 'estimate() shows usage increase after app is cached');
+</script>
+</html>
diff --git a/storage/estimate-usage-details-caches.https.tentative.any.js b/storage/estimate-usage-details-caches.https.tentative.any.js
new file mode 100644
index 0000000..bf889f8
--- /dev/null
+++ b/storage/estimate-usage-details-caches.https.tentative.any.js
@@ -0,0 +1,20 @@
+// META: title=StorageManager: estimate() for caches
+
+promise_test(async t => {
+  let estimate = await navigator.storage.estimate();
+
+  const cachesUsageBeforeCreate = estimate.usageDetails.caches || 0;
+
+  const cacheName = 'testCache';
+  const cache = await caches.open(cacheName);
+  t.add_cleanup(() => caches.delete(cacheName));
+
+  await cache.put('/test.json', new Response('x'.repeat(1024*1024)));
+
+  estimate = await navigator.storage.estimate();
+  assert_true('caches' in estimate.usageDetails);
+  const cachesUsageAfterPut = estimate.usageDetails.caches;
+  assert_greater_than(
+      cachesUsageAfterPut, cachesUsageBeforeCreate,
+      'estimated usage should increase after value is stored');
+}, 'estimate() shows usage increase after large value is stored');
diff --git a/storage/estimate-usage-details.https.tentative.any.js b/storage/estimate-usage-details.https.tentative.any.js
new file mode 100644
index 0000000..2a1cea5
--- /dev/null
+++ b/storage/estimate-usage-details.https.tentative.any.js
@@ -0,0 +1,12 @@
+// META: title=StorageManager: estimate() should have usage details
+
+promise_test(async t => {
+  const estimate = await navigator.storage.estimate();
+  assert_equals(typeof estimate, 'object');
+  assert_true('usage' in estimate);
+  assert_equals(typeof estimate.usage, 'number');
+  assert_true('quota' in estimate);
+  assert_equals(typeof estimate.quota, 'number');
+  assert_true('usageDetails' in estimate);
+  assert_equals(typeof estimate.usageDetails, 'object');
+}, 'estimate() resolves to dictionary with members, including usageDetails');
diff --git a/storage/resources/appcache.manifest b/storage/resources/appcache.manifest
new file mode 100644
index 0000000..ce90baf
--- /dev/null
+++ b/storage/resources/appcache.manifest
@@ -0,0 +1,3 @@
+CACHE MANIFEST
+# iframe_with_appcache_manifest.html references this manifest to get cached in
+# AppCache.
diff --git a/storage/resources/iframe_with_appcache_manifest.html b/storage/resources/iframe_with_appcache_manifest.html
new file mode 100644
index 0000000..8365ce4
--- /dev/null
+++ b/storage/resources/iframe_with_appcache_manifest.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html manifest="appcache.manifest">
+<title>Iframe that will be cached using application cache.</title>
+<meta charset='utf-8'>
+<link rel='author' href='jarrydg@chromium.org' title='Jarryd Goodman'>
+<script>
+(async () => {
+  const initPromise = new Promise(resolve => {
+    applicationCache.addEventListener('cached', resolve);
+    applicationCache.addEventListener('noupdate', resolve);
+  });
+  await initPromise;
+
+  window.parent.postMessage('document cached');
+})();
+</script>
+</html>
diff --git a/tools/ci/check_stability.py b/tools/ci/check_stability.py
deleted file mode 100644
index a71e095..0000000
--- a/tools/ci/check_stability.py
+++ /dev/null
@@ -1,318 +0,0 @@
-from __future__ import print_function
-
-import argparse
-import logging
-import os
-import subprocess
-import sys
-from ConfigParser import SafeConfigParser
-
-here = os.path.dirname(__file__)
-wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir))
-sys.path.insert(0, wpt_root)
-
-from tools.wpt import run as wptrun, testfiles
-from tools.wpt.testfiles import get_git_cmd
-from tools.wpt.virtualenv import Virtualenv
-from tools.wpt.utils import Kwargs
-from tools.wpt.run import create_parser, setup_wptrunner
-from tools.wpt import markdown
-from tools import localpaths  # noqa: F401
-
-logger = None
-run_step, write_inconsistent, write_slow_tests, write_results = None, None, None, None
-wptrunner = None
-
-def setup_logging():
-    """Set up basic debug logger."""
-    global logger
-    logger = logging.getLogger(here)
-    handler = logging.StreamHandler(sys.stdout)
-    formatter = logging.Formatter(logging.BASIC_FORMAT, None)
-    handler.setFormatter(formatter)
-    logger.addHandler(handler)
-    logger.setLevel(logging.DEBUG)
-    wptrun.setup_logging({})
-
-def do_delayed_imports():
-    global wptrunner, run_step, write_inconsistent, write_slow_tests, write_results
-    from wptrunner import wptrunner
-    from wptrunner.stability import run_step, write_inconsistent, write_slow_tests, write_results
-
-
-class TravisFold(object):
-    """Context for TravisCI folding mechanism. Subclasses object.
-
-    See: https://blog.travis-ci.com/2013-05-22-improving-build-visibility-log-folds/
-    """
-
-    def __init__(self, name):
-        """Register TravisCI folding section name."""
-        self.name = name
-
-    def __enter__(self):
-        """Emit fold start syntax."""
-        print("travis_fold:start:%s" % self.name, file=sys.stderr)
-
-    def __exit__(self, type, value, traceback):
-        """Emit fold end syntax."""
-        print("travis_fold:end:%s" % self.name, file=sys.stderr)
-
-
-class FilteredIO(object):
-    """Wrap a file object, invoking the provided callback for every call to
-    `write` and only proceeding with the operation when that callback returns
-    True."""
-    def __init__(self, original, on_write):
-        self.original = original
-        self.on_write = on_write
-
-    def __getattr__(self, name):
-        return getattr(self.original, name)
-
-    def disable(self):
-        self.write = lambda msg: None
-
-    def write(self, msg):
-        encoded = msg.encode("utf8", "backslashreplace").decode("utf8")
-        if self.on_write(self.original, encoded) is True:
-            self.original.write(encoded)
-
-
-def replace_streams(capacity, warning_msg):
-    # Value must be boxed to support modification from inner function scope
-    count = [0]
-    capacity -= 2 + len(warning_msg)
-    stderr = sys.stderr
-
-    def on_write(handle, msg):
-        length = len(msg)
-        count[0] += length
-
-        if count[0] > capacity:
-            wrapped_stdout.disable()
-            wrapped_stderr.disable()
-            handle.write(msg[0:capacity - count[0]])
-            handle.flush()
-            stderr.write("\n%s\n" % warning_msg)
-            return False
-
-        return True
-
-    # Store local references to the replaced streams to guard against the case
-    # where other code replace the global references.
-    sys.stdout = wrapped_stdout = FilteredIO(sys.stdout, on_write)
-    sys.stderr = wrapped_stderr = FilteredIO(sys.stderr, on_write)
-
-
-def call(*args):
-    """Log terminal command, invoke it as a subprocess.
-
-    Returns a bytestring of the subprocess output if no error.
-    """
-    logger.debug(" ".join(args))
-    try:
-        return subprocess.check_output(args)
-    except subprocess.CalledProcessError as e:
-        logger.critical("%s exited with return code %i" %
-                        (e.cmd, e.returncode))
-        logger.critical(e.output)
-        raise
-
-
-def fetch_wpt(user, *args):
-    git = get_git_cmd(wpt_root)
-    git("fetch", "https://github.com/%s/web-platform-tests.git" % user, *args)
-
-
-def get_sha1():
-    """ Get and return sha1 of current git branch HEAD commit."""
-    git = get_git_cmd(wpt_root)
-    return git("rev-parse", "HEAD").strip()
-
-
-def deepen_checkout(user):
-    """Convert from a shallow checkout to a full one"""
-    fetch_args = [user, "+refs/heads/*:refs/remotes/origin/*"]
-    if os.path.exists(os.path.join(wpt_root, ".git", "shallow")):
-        fetch_args.insert(1, "--unshallow")
-    fetch_wpt(*fetch_args)
-
-
-def get_parser():
-    """Create and return script-specific argument parser."""
-    description = """Detect instabilities in new tests by executing tests
-    repeatedly and comparing results between executions."""
-    parser = argparse.ArgumentParser(description=description)
-    parser.add_argument("--user",
-                        action="store",
-                        # Travis docs say do not depend on USER env variable.
-                        # This is a workaround to get what should be the same value
-                        default=os.environ.get("TRAVIS_REPO_SLUG", "w3c").split('/')[0],
-                        help="Travis user name")
-    parser.add_argument("--output-bytes",
-                        action="store",
-                        type=int,
-                        help="Maximum number of bytes to write to standard output/error")
-    parser.add_argument("--metadata",
-                        dest="metadata_root",
-                        action="store",
-                        default=wpt_root,
-                        help="Directory that will contain MANIFEST.json")
-    parser.add_argument("--config-file",
-                        action="store",
-                        type=str,
-                        help="Location of ini-formatted configuration file",
-                        default="check_stability.ini")
-    parser.add_argument("--rev",
-                        action="store",
-                        default=None,
-                        help="Commit range to use")
-    return parser
-
-
-def pr():
-    pr = os.environ.get("TRAVIS_PULL_REQUEST", "false")
-    return pr if pr != "false" else None
-
-
-def get_changed_files(manifest_path, rev, ignore_changes):
-    if not rev:
-        branch_point = testfiles.branch_point()
-        revish = "%s..HEAD" % branch_point
-    else:
-        revish = rev
-
-    files_changed, files_ignored = testfiles.files_changed(revish, ignore_changes)
-
-    if files_ignored:
-        logger.info("Ignoring %s changed files:\n%s" %
-                    (len(files_ignored), "".join(" * %s\n" % item for item in files_ignored)))
-
-    tests_changed, files_affected = testfiles.affected_testfiles(files_changed,
-                                                                 manifest_path=manifest_path)
-
-    return tests_changed, files_affected
-
-
-def main():
-    """Perform check_stability functionality and return exit code."""
-
-    venv = Virtualenv(os.environ.get("VIRTUAL_ENV", os.path.join(wpt_root, "_venv")))
-    venv.install_requirements(os.path.join(wpt_root, "tools", "wptrunner", "requirements.txt"))
-
-    args, wpt_args = get_parser().parse_known_args()
-    return run(venv, wpt_args, **vars(args))
-
-
-def run(venv, wpt_args, **kwargs):
-    do_delayed_imports()
-
-    setup_logging()
-
-    retcode = 0
-
-    wpt_args = create_parser().parse_args(wpt_args)
-
-    with open(kwargs["config_file"], 'r') as config_fp:
-        config = SafeConfigParser()
-        config.readfp(config_fp)
-        ignore_changes = set(config.get("file detection", "ignore_changes").split())
-
-    if kwargs["output_bytes"] is not None:
-        replace_streams(kwargs["output_bytes"],
-                        "Log reached capacity (%s bytes); output disabled." % kwargs["output_bytes"])
-
-
-    wpt_args.metadata_root = kwargs["metadata_root"]
-    try:
-        os.makedirs(wpt_args.metadata_root)
-    except OSError:
-        pass
-
-    pr_number = pr()
-
-    with TravisFold("browser_setup"):
-        logger.info(markdown.format_comment_title(wpt_args.product))
-
-        if pr is not None:
-            deepen_checkout(kwargs["user"])
-
-        # Ensure we have a branch called "master"
-        fetch_wpt(kwargs["user"], "master:master")
-
-        head_sha1 = get_sha1()
-        logger.info("Testing web-platform-tests at revision %s" % head_sha1)
-
-        wpt_kwargs = Kwargs(vars(wpt_args))
-
-        if not wpt_kwargs["test_list"]:
-            manifest_path = os.path.join(wpt_kwargs["metadata_root"], "MANIFEST.json")
-            tests_changed, files_affected = get_changed_files(manifest_path, kwargs["rev"],
-                                                              ignore_changes)
-
-            if not (tests_changed or files_affected):
-                logger.info("No tests changed")
-                return 0
-
-            if tests_changed:
-                logger.debug("Tests changed:\n%s" % "".join(" * %s\n" % item for item in tests_changed))
-
-            if files_affected:
-                logger.debug("Affected tests:\n%s" % "".join(" * %s\n" % item for item in files_affected))
-
-            wpt_kwargs["test_list"] = list(tests_changed | files_affected)
-
-        do_delayed_imports()
-
-        wpt_kwargs["prompt"] = False
-        wpt_kwargs["install_browser"] = wpt_kwargs["product"].split(":")[0] == "firefox"
-
-        wpt_kwargs["pause_after_test"] = False
-        wpt_kwargs["verify_log_full"] = False
-        if wpt_kwargs["repeat"] == 1:
-            wpt_kwargs["repeat"] = 10
-        wpt_kwargs["headless"] = False
-
-        wpt_kwargs["log_tbpl"] = [sys.stdout]
-
-        wpt_kwargs = setup_wptrunner(venv, **wpt_kwargs)
-
-        logger.info("Using binary %s" % wpt_kwargs["binary"])
-
-
-    with TravisFold("running_tests"):
-        logger.info("Starting tests")
-
-        wpt_logger = wptrunner.logger
-        results, inconsistent, slow, iterations = run_step(wpt_logger, wpt_kwargs["repeat"], True, {}, **wpt_kwargs)
-
-    if results:
-        if inconsistent:
-            write_inconsistent(logger.error, inconsistent, iterations)
-            retcode = 2
-        elif slow:
-            write_slow_tests(logger.error, slow)
-            retcode = 2
-        else:
-            logger.info("All results were stable\n")
-        with TravisFold("full_results"):
-            write_results(logger.info, results, iterations,
-                          pr_number=pr_number,
-                          use_details=True)
-    else:
-        logger.info("No tests run.")
-        # Be conservative and only return errors when we know for sure tests are changed.
-        if tests_changed:
-            retcode = 3
-
-    return retcode
-
-
-if __name__ == "__main__":
-    try:
-        sys.exit(main())
-    except Exception:
-        import traceback
-        traceback.print_exc()
-        sys.exit(1)
diff --git a/tools/ci/commands.json b/tools/ci/commands.json
index 361c9e4..841fd85 100644
--- a/tools/ci/commands.json
+++ b/tools/ci/commands.json
@@ -6,20 +6,6 @@
     "help": "List test jobs that should run for a set of commits",
     "virtualenv": false
   },
-  "check-stability": {
-    "path": "check_stability.py",
-    "script": "run",
-    "parser": "get_parser",
-    "parse_known": true,
-    "help": "Check test stability",
-    "virtualenv": true,
-    "install": [
-      "requests"
-    ],
-    "requirements": [
-      "../wptrunner/requirements.txt"
-    ]
-  },
   "make-hosts-file": {
     "path": "make_hosts_file.py",
     "script": "run",
diff --git a/tools/ci/start.sh b/tools/ci/start.sh
index 9f621b2..580bfb9 100644
--- a/tools/ci/start.sh
+++ b/tools/ci/start.sh
@@ -28,3 +28,4 @@
 fi
 
 sudo Xvfb $DISPLAY -screen 0 ${SCREEN_WIDTH}x${SCREEN_HEIGHT}x${SCREEN_DEPTH} &
+sudo fluxbox -display $DISPLAY &
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index 20f6ef2..53564ac 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -11,6 +11,7 @@
     ca-certificates \
     dbus-x11 \
     earlyoom \
+    fluxbox \
     gdebi \
     git \
     locales \
diff --git a/tools/requirements_flake8.txt b/tools/requirements_flake8.txt
index d2e7dd6..00d182e 100644
--- a/tools/requirements_flake8.txt
+++ b/tools/requirements_flake8.txt
@@ -1,4 +1,4 @@
 flake8==3.6.0
 pycodestyle==2.4.0
-pyflakes==2.0.0
+pyflakes==2.1.0
 pep8-naming==0.7.0
diff --git a/tools/wpt/tests/test_wpt.py b/tools/wpt/tests/test_wpt.py
index bf6250f..1ddac67 100644
--- a/tools/wpt/tests/test_wpt.py
+++ b/tools/wpt/tests/test_wpt.py
@@ -412,6 +412,5 @@
 
 # The following commands are slow running and used implicitly in other CI
 # jobs, so we skip them here:
-# wpt check-stability
 # wpt manifest
 # wpt lint
diff --git a/tools/wptrunner/wptrunner/browsers/firefox.py b/tools/wptrunner/wptrunner/browsers/firefox.py
index 3b5c96c..67d5b6e 100644
--- a/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -76,6 +76,7 @@
             "certutil_binary": kwargs["certutil_binary"],
             "ca_certificate_path": config.ssl_config["ca_cert_path"],
             "e10s": kwargs["gecko_e10s"],
+            "lsan_dir": kwargs["lsan_dir"],
             "stackfix_dir": kwargs["stackfix_dir"],
             "binary_args": kwargs["binary_args"],
             "timeout_multiplier": get_timeout_multiplier(test_type,
@@ -181,7 +182,7 @@
 
     def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
-                 ca_certificate_path=None, e10s=False, stackfix_dir=None,
+                 ca_certificate_path=None, e10s=False, lsan_dir=None, stackfix_dir=None,
                  binary_args=None, timeout_multiplier=None, leak_check=False, asan=False,
                  stylo_threads=1, chaos_mode_flags=None, config=None, headless=None, **kwargs):
         Browser.__init__(self, logger)
@@ -210,6 +211,7 @@
             self.init_timeout = self.init_timeout * timeout_multiplier
 
         self.asan = asan
+        self.lsan_dir = lsan_dir
         self.lsan_allowed = None
         self.lsan_max_stack_depth = None
         self.leak_check = leak_check
@@ -243,7 +245,7 @@
         env = test_environment(xrePath=os.path.dirname(self.binary),
                                debugger=self.debug_info is not None,
                                log=self.logger,
-                               lsanPath=self.prefs_root)
+                               lsanPath=self.lsan_dir)
 
         env["STYLO_THREADS"] = str(self.stylo_threads)
         if self.chaos_mode_flags is not None:
diff --git a/tools/wptrunner/wptrunner/metadata.py b/tools/wptrunner/wptrunner/metadata.py
index 94560cd..b170fc6 100644
--- a/tools/wptrunner/wptrunner/metadata.py
+++ b/tools/wptrunner/wptrunner/metadata.py
@@ -479,7 +479,7 @@
         prop = prop_intern.get((packed & 0xF000) >> 12)
 
         value_idx = (packed & 0x0F00) >> 8
-        if value_idx is 0:
+        if value_idx == 0:
             value = self.raw_data[idx]
         else:
             value = status_intern.get(value_idx)
diff --git a/tools/wptrunner/wptrunner/wptcommandline.py b/tools/wptrunner/wptrunner/wptcommandline.py
index 8443a01..0a3a8c2 100644
--- a/tools/wptrunner/wptrunner/wptcommandline.py
+++ b/tools/wptrunner/wptrunner/wptcommandline.py
@@ -256,6 +256,8 @@
                              help="Run tests without electrolysis preferences")
     gecko_group.add_argument("--stackfix-dir", dest="stackfix_dir", action="store",
                              help="Path to directory containing assertion stack fixing scripts")
+    gecko_group.add_argument("--lsan-dir", dest="lsan_dir", action="store",
+                             help="Path to directory containing LSAN suppressions file")
     gecko_group.add_argument("--setpref", dest="extra_prefs", action='append',
                              default=[], metavar="PREF=VALUE",
                              help="Defines an extra user preference (overrides those in prefs_root)")
@@ -527,6 +529,9 @@
     if kwargs["reftest_internal"] is None:
         kwargs["reftest_internal"] = True
 
+    if kwargs["lsan_dir"] is None:
+        kwargs["lsan_dir"] = kwargs["prefs_root"]
+
     return kwargs
 
 
diff --git a/wake-lock/wakelock-supported-by-feature-policy.html b/wake-lock/wakelock-supported-by-feature-policy.html
new file mode 100644
index 0000000..d6289ff
--- /dev/null
+++ b/wake-lock/wakelock-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that wake-lock is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://w3c.github.io/wake-lock/#dfn-wake-lock-feature">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('wake-lock', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise wake-lock.');
+</script>
diff --git a/webdriver/tests/new_window/new_tab.py b/webdriver/tests/new_window/new_tab.py
index fbb249f..0bf9449 100644
--- a/webdriver/tests/new_window/new_tab.py
+++ b/webdriver/tests/new_window/new_tab.py
@@ -26,7 +26,7 @@
     value = assert_success(response)
     assert value["type"] == "tab"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert session.url == "about:blank"
 
 
@@ -35,7 +35,7 @@
     value = assert_success(response)
     assert value["type"] == "tab"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert window_name(session) == ""
 
 
@@ -44,5 +44,5 @@
     value = assert_success(response)
     assert value["type"] == "tab"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert opener(session) is None
diff --git a/webdriver/tests/new_window/new_window.py b/webdriver/tests/new_window/new_window.py
index eb4db67..8b12248 100644
--- a/webdriver/tests/new_window/new_window.py
+++ b/webdriver/tests/new_window/new_window.py
@@ -26,7 +26,7 @@
     value = assert_success(response)
     assert value["type"] == "window"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert session.url == "about:blank"
 
 
@@ -35,7 +35,7 @@
     value = assert_success(response)
     assert value["type"] == "window"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert window_name(session) == ""
 
 
@@ -44,5 +44,5 @@
     value = assert_success(response)
     assert value["type"] == "window"
 
-    session.handle = value["handle"]
+    session.window_handle = value["handle"]
     assert opener(session) is None
diff --git a/webrtc-quic/RTCQuicTransport.https.html b/webrtc-quic/RTCQuicTransport.https.html
index c64ed6a..82427e6 100644
--- a/webrtc-quic/RTCQuicTransport.https.html
+++ b/webrtc-quic/RTCQuicTransport.https.html
@@ -5,6 +5,7 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="../webrtc/RTCIceTransport-extension-helper.js"></script>
 <script src="RTCQuicTransport-helper.js"></script>
+<script src="../webrtc/dictionary-helper.js"></script>
 <script>
 'use strict';
 
@@ -21,6 +22,7 @@
 //   makeStandaloneQuicTransport
 //   makeAndStartTwoQuicTransports
 //   makeTwoConnectedQuicTransports
+//   sleep
 
 test(t => {
   const iceTransport = makeIceTransport(t);
@@ -174,5 +176,93 @@
   assert_not_equals(update_key, new Uint8Array(new_key));
 }, 'Cannot mutate key retrieved from getKey().');
 
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      makeAndStartTwoQuicTransports(t);
+  const stats = await localQuicTransport.getStats();
+  assert_number_field(stats, 'timestamp');
+  assert_unsigned_int_field(stats, 'bytesSent');
+  assert_unsigned_int_field(stats, 'packetsSent');
+  assert_unsigned_int_field(stats, 'streamBytesSent');
+  assert_unsigned_int_field(stats, 'streamBytesReceived');
+  assert_unsigned_int_field(stats, 'numOutgoingStreamsCreated');
+  assert_unsigned_int_field(stats, 'numIncomingStreamsCreated');
+  assert_unsigned_int_field(stats, 'bytesReceived');
+  assert_unsigned_int_field(stats, 'packetsReceived');
+  assert_unsigned_int_field(stats, 'packetsProcessed');
+  assert_unsigned_int_field(stats, 'bytesRetransmitted');
+  assert_unsigned_int_field(stats, 'packetsRetransmitted');
+  assert_unsigned_int_field(stats, 'packetsLost');
+  assert_unsigned_int_field(stats, 'packetsDropped');
+  assert_unsigned_int_field(stats, 'cryptoRetransmitCount');
+  assert_unsigned_int_field(stats, 'minRttUs');
+  assert_unsigned_int_field(stats, 'smoothedRttUs');
+  assert_unsigned_int_field(stats, 'maxPacketSize');
+  assert_unsigned_int_field(stats, 'maxReceivedPacketSize');
+  assert_unsigned_int_field(stats, 'estimatedBandwidthBps');
+  assert_unsigned_int_field(stats, 'packetsReordered');
+  assert_unsigned_int_field(stats, 'blockedFramesReceived');
+  assert_unsigned_int_field(stats, 'blockedFramesSent');
+  assert_unsigned_int_field(stats, 'connectivityProbingPacketsReceived');
+}, 'Stats returned by getStats() are present.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.write({ finish: true });
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  await remoteWatcher.wait_for('quicstream');
+  const localStats = await localQuicTransport.getStats();
+  const remoteStats = await remoteQuicTransport.getStats();
+  assert_equals(localStats.numOutgoingStreamsCreated, 1);
+  assert_equals(localStats.numIncomingStreamsCreated, 0);
+  assert_equals(remoteStats.numOutgoingStreamsCreated, 0);
+  assert_equals(remoteStats.numIncomingStreamsCreated, 1);
+}, 'getStats() returns proper stream counts after creating streams.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      makeAndStartTwoQuicTransports(t);
+  const stats1 = await localQuicTransport.getStats();
+  await new Promise(resolve => t.step_timeout(resolve, 20));
+  const stats2 = await localQuicTransport.getStats();
+  assert_greater_than(stats2.timestamp, stats1.timestamp);
+}, 'Two separate stats returned by getStats() give different timestamps.');
+
+promise_test(async t => {
+  const quicTransport = makeStandaloneQuicTransport(t);
+  const promise = quicTransport.getStats();
+  promise_rejects(t, 'InvalidStateError', promise);
+}, 'getStats() promises immediately rejected with InvalidStateError ' +
+    `if called before 'connecting'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const promise = localQuicTransport.getStats();
+  localQuicTransport.stop();
+  promise_rejects(t, 'InvalidStateError', promise);
+}, 'getStats() promises rejected  with InvalidStateError if stop() ' +
+   'is called before being fulfilled.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const promise = localQuicTransport.getStats();
+  localQuicTransport.transport.stop();
+  promise_rejects(t, 'InvalidStateError', promise);
+}, 'getStats() promises rejected  with InvalidStateError if ' +
+   'RTCIceTransport calls stop() before being fulfilled.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  localQuicTransport.transport.stop();
+  const promise = localQuicTransport.getStats();
+  promise_rejects(t, 'InvalidStateError', promise);
+}, 'getStats() promises immediately rejected if called after' +
+   `'closed' state.`);
+
 </script>
 
diff --git a/webrtc-stats/idlharness.window.js b/webrtc-stats/idlharness.window.js
index 2374120..d98712f 100644
--- a/webrtc-stats/idlharness.window.js
+++ b/webrtc-stats/idlharness.window.js
@@ -7,7 +7,7 @@
 
 idl_test(
   ['webrtc-stats'],
-  [], // No deps
+  ['webrtc'],
   idl_array => {
     // No interfaces to test
   }
diff --git a/webrtc/RTCPeerConnection-iceConnectionState.html b/webrtc/RTCPeerConnection-iceConnectionState.html
index 4071033..b647b3d 100644
--- a/webrtc/RTCPeerConnection-iceConnectionState.html
+++ b/webrtc/RTCPeerConnection-iceConnectionState.html
@@ -64,6 +64,12 @@
     assert_equals(pc.iceConnectionState, 'new');
   }, 'Initial iceConnectionState should be new');
 
+  test(t => {
+    const pc = new RTCPeerConnection();
+    pc.close();
+    assert_equals(pc.iceConnectionState, 'closed');
+  }, 'Closing the connection should set iceConnectionState to closed');
+
   /*
     4.4.4 RTCIceConnectionState Enum
       checking
@@ -106,6 +112,35 @@
     const pc1 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
     const pc2 = new RTCPeerConnection();
+    t.add_cleanup(() => pc2.close());
+
+    let had_checking = false;
+
+    const onIceConnectionStateChange = t.step_func(() => {
+      const {iceConnectionState} = pc1;
+      if (iceConnectionState === 'checking') {
+        had_checking = true;
+      } else if (iceConnectionState === 'connected' ||
+                 iceConnectionState === 'completed') {
+        assert_true(had_checking, 'state should pass checking before' +
+                                  ' reaching connected or completed');
+        t.done();
+      }
+    });
+
+    pc1.createDataChannel('test');
+
+    pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
+
+    exchangeIceCandidates(pc1, pc2);
+    doSignalingHandshake(pc1, pc2);
+  }, 'connection with one data channel should eventually have connected or ' +
+     'completed connection state');
+
+async_test(t => {
+    const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+    const pc2 = new RTCPeerConnection();
 
     t.add_cleanup(() => pc2.close());
 
@@ -116,19 +151,22 @@
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'checking',
-          'Expect ICE transport to be in checking state when iceConnectionState is checking');
+          'Expect ICE transport to be in checking state when' +
+          ' iceConnectionState is checking');
 
       } else if(iceConnectionState === 'connected') {
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'connected',
-          'Expect ICE transport to be in connected state when iceConnectionState is connected');
+          'Expect ICE transport to be in connected state when' +
+          ' iceConnectionState is connected');
 
       } else if(iceConnectionState === 'completed') {
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'completed',
-          'Expect ICE transport to be in connected state when iceConnectionState is completed');
+          'Expect ICE transport to be in connected state when' +
+          ' iceConnectionState is completed');
       }
     });
 
@@ -141,7 +179,8 @@
 
     exchangeIceCandidates(pc1, pc2);
     doSignalingHandshake(pc1, pc2);
-  }, 'connection with one data channel should eventually have connected connection state');
+  }, 'connection with one data channel should eventually ' +
+     'have connected connection state');
 
   /*
     TODO
diff --git a/webusb/usb-supported-by-feature-policy.html b/webusb/usb-supported-by-feature-policy.html
new file mode 100644
index 0000000..d5b585a
--- /dev/null
+++ b/webusb/usb-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that usb is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://wicg.github.io/webusb/#feature-policy">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('usb', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise usb.');
+</script>
diff --git a/webvr/webvr-supported-by-feature-policy.html b/webvr/webvr-supported-by-feature-policy.html
new file mode 100644
index 0000000..9487c35
--- /dev/null
+++ b/webvr/webvr-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that xr is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://immersive-web.github.io/webxr/#feature-policy">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('xr', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise xr.');
+</script>
diff --git a/xhr/sync-xhr-supported-by-feature-policy.html b/xhr/sync-xhr-supported-by-feature-policy.html
new file mode 100644
index 0000000..45588bf
--- /dev/null
+++ b/xhr/sync-xhr-supported-by-feature-policy.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test that sync-xhr is advertised in the feature list</title>
+<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features">
+<link rel="help" href="https://xhr.spec.whatwg.org/#feature-policy-integration">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_in_array('sync-xhr', document.featurePolicy.features());
+}, 'document.featurePolicy.features should advertise sync-xhr.');
+</script>