Merge pull request #12417 from muhammedyusuf-sermet/contain-style-test-updates

CSS Containment - Contain:style Scoping Test Updates
diff --git a/2dcontext/imagebitmap/canvas-createImageBitmap-resize.html b/2dcontext/imagebitmap/canvas-createImageBitmap-resize.html
new file mode 100644
index 0000000..ea30328
--- /dev/null
+++ b/2dcontext/imagebitmap/canvas-createImageBitmap-resize.html
@@ -0,0 +1,159 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function checkNoCrop(imageBitmap)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 50;
+    canvas.height = 50;
+    var ctx = canvas.getContext("2d");
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    ctx.drawImage(imageBitmap, 0, 0);
+    var d = ctx.getImageData(0, 0, 1, 1).data;
+    assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+    d = ctx.getImageData(39, 0, 1, 1).data;
+    assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+    d = ctx.getImageData(0, 39, 1, 1).data;
+    assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+    d = ctx.getImageData(39, 39, 1, 1).data;
+    assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+    d = ctx.getImageData(41, 41, 1, 1).data;
+    assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function checkCrop(imageBitmap)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = 50;
+    canvas.height = 50;
+    var ctx = canvas.getContext("2d");
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    ctx.drawImage(imageBitmap, 0, 0);
+    var d = ctx.getImageData(0, 0, 1, 1).data;
+    assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red.");
+    d = ctx.getImageData(19, 0, 1, 1).data;
+    assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green.");
+    d = ctx.getImageData(0, 19, 1, 1).data;
+    assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue.");
+    d = ctx.getImageData(19, 19, 1, 1).data;
+    assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black.");
+    d = ctx.getImageData(21, 21, 1, 1).data;
+    assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black.");
+}
+
+function compareBitmaps(bitmap1, bitmap2)
+{
+    var canvas1 = document.createElement("canvas");
+    var canvas2 = document.createElement("canvas");
+    canvas1.width = 50;
+    canvas1.height = 50;
+    canvas2.width = 50;
+    canvas2.height = 50;
+    var ctx1 = canvas1.getContext("2d");
+    var ctx2 = canvas2.getContext("2d");
+    ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
+    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
+    ctx1.drawImage(bitmap1, 0, 0);
+    ctx2.drawImage(bitmap2, 0, 0);
+    var data1 = ctx1.getImageData(0, 0, 50, 50).data;
+    var data2 = ctx2.getImageData(0, 0, 50, 50).data;
+    var dataMatched = true;
+    for (var i = 0; i < data1.length; i++) {
+        if (data1[i] != data2[i]) {
+            dataMatched = false;
+            break;
+        }
+    }
+    assert_false(dataMatched);
+}
+
+function testImageBitmap(source)
+{
+    return Promise.all([
+        createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "high"}),
+        createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "medium"}),
+        createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "low"}),
+        createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "pixelated"}),
+        createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "high"}),
+        createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "medium"}),
+        createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "low"}),
+        createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "pixelated"}),
+    ]).then(([noCropHigh, noCropMedium, noCropLow, noCropPixelated, cropHigh, cropMedium, cropLow, cropPixelated]) => {
+        checkNoCrop(noCropHigh);
+        checkNoCrop(noCropMedium);
+        checkNoCrop(noCropLow);
+        checkNoCrop(noCropPixelated);
+        checkCrop(cropHigh);
+        checkCrop(cropMedium);
+        checkCrop(cropLow);
+        checkCrop(cropPixelated);
+        // Brute-force comparison among all bitmaps is too expensive
+        compareBitmaps(noCropHigh, noCropMedium);
+        compareBitmaps(noCropLow, noCropPixelated);
+        compareBitmaps(cropHigh, cropMedium);
+        compareBitmaps(cropLow, cropPixelated);
+    });
+}
+
+function initializeTestCanvas()
+{
+    var testCanvas = document.createElement("canvas");
+    testCanvas.width = 20;
+    testCanvas.height = 20;
+    var testCtx = testCanvas.getContext("2d");
+    testCtx.fillStyle = "rgb(255, 0, 0)";
+    testCtx.fillRect(0, 0, 10, 10);
+    testCtx.fillStyle = "rgb(0, 255, 0)";
+    testCtx.fillRect(10, 0, 10, 10);
+    testCtx.fillStyle = "rgb(0, 0, 255)";
+    testCtx.fillRect(0, 10, 10, 10);
+    testCtx.fillStyle = "rgb(0, 0, 0)";
+    testCtx.fillRect(10, 10, 10, 10);
+    return testCanvas;
+}
+
+// Blob
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", '/images/pattern.png');
+        xhr.responseType = 'blob';
+        xhr.send();
+        xhr.onload = function() {
+            resolve(xhr.response);
+        };
+    }).then(testImageBitmap);
+}, 'createImageBitmap from a Blob with resize option.');
+
+// HTMLCanvasElement
+promise_test(function() {
+    var testCanvas = initializeTestCanvas();
+    return testImageBitmap(testCanvas);
+}, 'createImageBitmap from a HTMLCanvasElement with resize option.');
+
+// HTMLImageElement
+promise_test(function() {
+    return new Promise((resolve, reject) => {
+        var image = new Image();
+        image.onload = function() {
+            resolve(image);
+        }
+        image.src = '/images/pattern.png'
+    }).then(testImageBitmap);
+}, 'createImageBitmap from a HTMLImageElement with resize option.');
+
+// ImageBitmap
+promise_test(function() {
+    var testCanvas = initializeTestCanvas();
+    return createImageBitmap(testCanvas).then(testImageBitmap);
+}, 'createImageBitmap from an ImageBitmap with resize option.');
+
+// ImageData
+promise_test(function() {
+    var canvas = initializeTestCanvas();
+    var ctx = canvas.getContext("2d");
+    var data = ctx.getImageData(0, 0, 20, 20);
+    return testImageBitmap(data);
+}, 'createImageBitmap from an ImageData with resize option.');
+</script>
diff --git a/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html b/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html
new file mode 100644
index 0000000..366d1fd
--- /dev/null
+++ b/2dcontext/imagebitmap/canvas-createImageBitmap-video-resize.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function createNewCanvas(width, height)
+{
+    var canvas = document.createElement("canvas");
+    canvas.width = width;
+    canvas.height = height;
+    var ctx = canvas.getContext("2d");
+    ctx.clearRect(0, 0, width, height);
+    return ctx;
+}
+
+function checkLowResult(imageBitmap, bw, bh, video, sx, sy, sw, sh)
+{
+    var ctx1 = createNewCanvas(bw, bh);
+    var ctx2 = createNewCanvas(bw, bh);
+    ctx1.drawImage(imageBitmap, 0, 0);
+    ctx2.drawImage(video, sx, sy, sw, sh, 0, 0, bw, bh);
+    var data1 = ctx1.getImageData(0, 0, bw, bh).data;
+    var data2 = ctx2.getImageData(0, 0, bw, bh).data;
+    var dataMatched = true;
+    for (var i = 0; i < data1.length; i++) {
+        // data1[i] is strictly the same as data2[i] on software rendering.
+        // But on GPU, the difference could be quite significant.
+        if (Math.abs(data1[i] - data2[i]) > 18) {
+            dataMatched = false;
+            break;
+        }
+    }
+    assert_true(dataMatched);
+}
+
+function generateTest()
+{
+    bitmapWidth = video.videoWidth/2;
+    bitmapHeight = video.videoHeight/2;
+    return Promise.all([
+        createImageBitmap(video, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+        createImageBitmap(video, 10, 10, bitmapWidth, bitmapHeight, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}),
+    ]).then(t.step_func_done(([noCropLow, cropLow]) => {
+        checkLowResult(noCropLow, bitmapWidth, bitmapHeight, video, 0, 0, video.videoWidth, video.videoHeight);
+        checkLowResult(cropLow, bitmapWidth, bitmapHeight, video, 10, 10, bitmapWidth, bitmapHeight);
+    }), t.step_func_done(function() {
+        assert_true(false, 'Promise rejected');
+    }));
+}
+
+var t = async_test('createImageBitmap(HTMLVideoElement) with resize option');
+
+// HTMLVideoElement
+var video = document.createElement("video");
+video.oncanplaythrough = t.step_func(function() {
+    return generateTest();
+});
+video.src = "/media/video.ogv";
+</script>
diff --git a/BackgroundSync/interfaces.https.any.js b/BackgroundSync/interfaces.https.any.js
index 325bef8..989139e 100644
--- a/BackgroundSync/interfaces.https.any.js
+++ b/BackgroundSync/interfaces.https.any.js
@@ -10,6 +10,5 @@
   ['service-workers', 'html', 'dom'],
   idlArray => {
     // TODO: Objects
-  },
-  'Background Sync interfaces.'
+  }
 );
diff --git a/FileAPI/idlharness.html b/FileAPI/idlharness.html
index eb55b65..db6592e 100644
--- a/FileAPI/idlharness.html
+++ b/FileAPI/idlharness.html
@@ -35,8 +35,7 @@
             FileList: ['document.querySelector("#fileChooser").files'],
             FileReader: ['new FileReader()']
           });
-        },
-        'Test FileAPI IDL implementation'
+        }
       );
     </script>
 
diff --git a/FileAPI/idlharness.worker.js b/FileAPI/idlharness.worker.js
index e65ee01..5bf82e0 100644
--- a/FileAPI/idlharness.worker.js
+++ b/FileAPI/idlharness.worker.js
@@ -16,7 +16,6 @@
       FileReader: ['new FileReader()'],
       FileReaderSync: ['new FileReaderSync()']
     });
-  },
-  'Test FileAPI IDL implementation'
+  }
 );
 done();
diff --git a/IndexedDB/idlharness.any.js b/IndexedDB/idlharness.any.js
index bf83066..efb8466 100644
--- a/IndexedDB/idlharness.any.js
+++ b/IndexedDB/idlharness.any.js
@@ -20,6 +20,5 @@
       IDBVersionChangeEvent: ['new IDBVersionChangeEvent("type")'],
       DOMStringList: [],
     });
-  },
-  'IndexedDB interfaces'
+  }
 );
diff --git a/WebCryptoAPI/idlharness.https.any.js b/WebCryptoAPI/idlharness.https.any.js
index f5095cb..b0ccebd 100644
--- a/WebCryptoAPI/idlharness.https.any.js
+++ b/WebCryptoAPI/idlharness.https.any.js
@@ -11,6 +11,5 @@
       Crypto: ['crypto'],
       SubtleCrypto: ['crypto.subtle']
     });
-  },
-  'WebCryptoAPI interfaces'
+  }
 );
diff --git a/accelerometer/idlharness.https.window.js b/accelerometer/idlharness.https.window.js
index f6cbcd9..1e041e9 100644
--- a/accelerometer/idlharness.https.window.js
+++ b/accelerometer/idlharness.https.window.js
@@ -14,6 +14,5 @@
       LinearAccelerationSensor: ['new LinearAccelerationSensor();'],
       GravitySensor: ['new GravitySensor();']
     });
-  },
-  'Test IDL implementation of Accelerometer Sensor'
+  }
 );
diff --git a/ambient-light/idlharness.https.window.js b/ambient-light/idlharness.https.window.js
index ea99fc1..211e6aa 100644
--- a/ambient-light/idlharness.https.window.js
+++ b/ambient-light/idlharness.https.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       AmbientLightSensor: ['new AmbientLightSensor()']
     });
-  },
-  'Test IDL implementation of Ambient Light Sensor'
+  }
 );
diff --git a/animation-worklet/idlharness.any.js b/animation-worklet/idlharness.any.js
new file mode 100644
index 0000000..e821a2f
--- /dev/null
+++ b/animation-worklet/idlharness.any.js
@@ -0,0 +1,17 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+// https://wicg.github.io/animation-worklet/
+
+idl_test(
+  ['animation-worklet'],
+  ['worklets', 'web-animations', 'html', 'cssom', 'dom'],
+  idl_array => {
+    idl_array.add_objects({
+      WorkletAnimation: ['new WorkletAnimation("name")'],
+      // TODO: WorkletGroupEffect
+    });
+  }
+);
diff --git a/animation-worklet/interfaces.any.js b/animation-worklet/interfaces.any.js
deleted file mode 100644
index 9b1d756..0000000
--- a/animation-worklet/interfaces.any.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-
-'use strict';
-
-// https://wicg.github.io/animation-worklet/
-
-promise_test(async () => {
-  const idl = await (await fetch('/interfaces/animation-worklet.idl')).text();
-  const html = await (await fetch('/interfaces/html.idl')).text();
-  const idlArray = new IdlArray();
-  idlArray.add_idls(idl);
-  idlArray.add_dependency_idls(html);
-  idlArray.test();
-  done();
-}, 'Test driver');
diff --git a/appmanifest/idlharness.window.js b/appmanifest/idlharness.window.js
index a877c76..55e8b98 100644
--- a/appmanifest/idlharness.window.js
+++ b/appmanifest/idlharness.window.js
@@ -13,6 +13,5 @@
       Window: ['window'],
       BeforeInstallPromptEvent: ['new BeforeInstallPromptEvent("type")'],
     });
-  },
-  'appmanifest interfaces'
+  }
 );
diff --git a/background-fetch/fetch.https.window.js b/background-fetch/fetch.https.window.js
index cca6328..f146212 100644
--- a/background-fetch/fetch.https.window.js
+++ b/background-fetch/fetch.https.window.js
@@ -74,7 +74,7 @@
   // Skip `downloaded`, as the transfer may have started already.
 
   const {type, results} = await getMessageFromServiceWorker();
-  assert_equals('backgroundfetched', type);
+  assert_equals('backgroundfetchsuccess', type);
   assert_equals(results.length, 1);
 
   assert_true(results[0].url.includes('resources/feature-name.txt'));
diff --git a/background-fetch/idlharness.https.any.js b/background-fetch/idlharness.https.any.js
index ad20477..f2c8a56 100644
--- a/background-fetch/idlharness.https.any.js
+++ b/background-fetch/idlharness.https.any.js
@@ -20,6 +20,5 @@
         BackgroundFetchUpdateEvent: ['new BackgroundFetchUpdateEvent("type")'],
       });
     }
-  },
-  'background-fetch interfaces'
+  }
 );
diff --git a/background-fetch/service_workers/sw-update-ui.js b/background-fetch/service_workers/sw-update-ui.js
index ae0ed09..5dec087 100644
--- a/background-fetch/service_workers/sw-update-ui.js
+++ b/background-fetch/service_workers/sw-update-ui.js
@@ -2,7 +2,7 @@
 
 async function updateUI(event) {
   let updateParams = [];
-  switch (event.id) {
+  switch (event.registration.id) {
     case 'update-once':
       updateParams = [{title: 'Title1'}];
       break;
@@ -16,7 +16,7 @@
            .catch(e => e.message);
 }
 
-self.addEventListener('backgroundfetched', event => {
+self.addEventListener('backgroundfetchsuccess', event => {
   event.waitUntil(updateUI(event)
       .then(update => sendMessageToDocument({ type: event.type, update })))
 });
diff --git a/background-fetch/service_workers/sw.js b/background-fetch/service_workers/sw.js
index 34ea5d4..10e17f5 100644
--- a/background-fetch/service_workers/sw.js
+++ b/background-fetch/service_workers/sw.js
@@ -1,3 +1,4 @@
+
 importScripts('sw-helpers.js');
 
 async function getFetchResult(settledFetch) {
@@ -11,7 +12,7 @@
   };
 }
 
-self.addEventListener('backgroundfetched', event => {
+self.addEventListener('backgroundfetchsuccess', event => {
   event.waitUntil(
     event.fetches.values()
       .then(fetches => Promise.all(fetches.map(fetch => getFetchResult(fetch))))
diff --git a/background-fetch/update-ui.https.window.js b/background-fetch/update-ui.https.window.js
index d561267..aed0bb8 100644
--- a/background-fetch/update-ui.https.window.js
+++ b/background-fetch/update-ui.https.window.js
@@ -2,7 +2,7 @@
 // META: script=resources/utils.js
 'use strict';
 
-// Covers functionality provided by BackgroundFetchUpdateEvent.updateUI().
+// Covers functionality provided by BackgroundFetchUpdateUIEvent.updateUI().
 //
 // https://wicg.github.io/background-fetch/#backgroundfetchupdateuievent
 
diff --git a/battery-status/battery-interface-idlharness.https.window.js b/battery-status/battery-interface-idlharness.https.window.js
index 3db3e16..b33c82e 100644
--- a/battery-status/battery-interface-idlharness.https.window.js
+++ b/battery-status/battery-interface-idlharness.https.window.js
@@ -6,7 +6,7 @@
 'use strict';
 
 idl_test(
-  ['battery'],
+  ['battery-status'],
   ['dom', 'html'],
   async idl_array => {
     idl_array.add_objects({
@@ -15,6 +15,5 @@
     })
 
     self.manager = await navigator.getBattery();
-  },
-  'Test IDL implementation of Battery Status API'
+  }
 );
diff --git a/bluetooth/idl/idlharness.tentative.https.window.js b/bluetooth/idl/idlharness.tentative.https.window.js
index 4bacd56..f4e6d95 100644
--- a/bluetooth/idl/idlharness.tentative.https.window.js
+++ b/bluetooth/idl/idlharness.tentative.https.window.js
@@ -20,6 +20,5 @@
       Bluetooth: ['navigator.bluetooth'],
       BluetoothAdvertisingEvent: ['event'],
     });
-  },
-  'web-bluetooth interfaces.'
+  }
 );
diff --git a/content-security-policy/embedded-enforcement/idlharness.window.js b/content-security-policy/embedded-enforcement/idlharness.window.js
index 6ac306c..2845f82 100644
--- a/content-security-policy/embedded-enforcement/idlharness.window.js
+++ b/content-security-policy/embedded-enforcement/idlharness.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       HTMLIFrameElement: ['document.createElement("iframe")'],
     });
-  },
-  'csp-embedded-enforcement IDL'
+  }
 );
diff --git a/content-security-policy/securitypolicyviolation/idlharness.window.js b/content-security-policy/securitypolicyviolation/idlharness.window.js
index 361282a..fc5e65d 100644
--- a/content-security-policy/securitypolicyviolation/idlharness.window.js
+++ b/content-security-policy/securitypolicyviolation/idlharness.window.js
@@ -14,6 +14,5 @@
         'new SecurityPolicyViolationEvent("securitypolicyviolation")'
       ]
     })
-  },
-  'Test Content Security Policy IDL implementation'
+  }
 );
diff --git a/content-security-policy/securitypolicyviolation/targeting.html b/content-security-policy/securitypolicyviolation/targeting.html
index 10b96e9..28ca32e 100644
--- a/content-security-policy/securitypolicyviolation/targeting.html
+++ b/content-security-policy/securitypolicyviolation/targeting.html
@@ -43,7 +43,7 @@
             .then(t.step_func(e => {
                 assert_equals(e.blockedURI, "inline");
                 assert_equals(e.lineNumber, 131);
-                assert_equals(e.columnNumber, 4);
+                assert_in_array(e.columnNumber, [4, 59]);
                 assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
                 return watcher.wait_for('securitypolicyviolation');
             }))
diff --git a/cookie-store/idlharness.tentative.https.html b/cookie-store/idlharness.tentative.https.html
index 864b699..fafe79a 100644
--- a/cookie-store/idlharness.tentative.https.html
+++ b/cookie-store/idlharness.tentative.https.html
@@ -17,7 +17,6 @@
       CookieStore: ['cookieStore'],
       CookieChangeEvent: ['new CookieChangeEvent("change")'],
     });
-  },
-  'Interface test'
+  }
 );
 </script>
diff --git a/css/css-animations/idlharness.html b/css/css-animations/idlharness.html
index b25b970..1d3ed2b 100644
--- a/css/css-animations/idlharness.html
+++ b/css/css-animations/idlharness.html
@@ -39,8 +39,8 @@
           CSSKeyframesRule: ['keyframes'],
           CSSKeyframeRule: ['keyframes.cssRules[0]'],
         });
-      },
-      'Test css-animations IDL implementation');
+      }
+    );
   </script>
 </body>
 
diff --git a/css/css-counter-styles/idlharness.html b/css/css-counter-styles/idlharness.html
index b36a5de..e914b41 100644
--- a/css/css-counter-styles/idlharness.html
+++ b/css/css-counter-styles/idlharness.html
@@ -28,7 +28,6 @@
       idl_array.add_objects({
         CSSCounterStyleRule: ['counter'],
       });
-    },
-    'css-counter-styles interfaces'
+    }
   );
 </script>
diff --git a/css/css-font-loading/idlharness.https.html b/css/css-font-loading/idlharness.https.html
index 9e3d45fb..61a99e0 100644
--- a/css/css-font-loading/idlharness.https.html
+++ b/css/css-font-loading/idlharness.https.html
@@ -18,7 +18,6 @@
       FontFaceSetLoadEvent: ['new FontFaceSetLoadEvent("type")'],
       FontFaceSet: ['document.fonts'],
     });
-  },
-  'css-font-loading interfaces'
+  }
 );
 </script>
diff --git a/css/css-images/idlharness.html b/css/css-images/idlharness.html
index ec9c358..17bccbd 100644
--- a/css/css-images/idlharness.html
+++ b/css/css-images/idlharness.html
@@ -13,7 +13,6 @@
     ['cssom'],
     idl_array => {
       // No objects,
-    },
-    'Test IDL implementation of css-masking'
+    }
   );
 </script>
diff --git a/css/css-logical/animation-004.html b/css/css-logical/animation-004.html
new file mode 100644
index 0000000..48cb58a
--- /dev/null
+++ b/css/css-logical/animation-004.html
@@ -0,0 +1,250 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Animating CSS logical properties using CSS Transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical/#box">
+<meta name="assert" content="The specified values of these properties are separate from the specified values of the parallel physical properties, but the flow-relative and physical properties share computed values.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../css-animations/support/testcommon.js"></script>
+
+<div id="log"></div>
+<div id="test"></div>
+<script>
+'use strict';
+
+const testEl = document.getElementById("test");
+
+function makeDeclaration(object = {}) {
+  return Object.entries(object).map(([prop, val]) => prop + ": " + val).join("; ");
+}
+
+/**
+ * Starts a CSS transition in testEl. By default, the transition will affect the properies
+ * specified in finalStyles, be linear, last 10 seconds and start halfway, but this can be
+ * overridden in baseStyles.
+ *
+ * @param t  The testharness.js Test object.
+ * @param baseStyles  A dictionary object with property names and values to set on the
+ *                    element before starting the transition.
+ * @param finalStyles  A dictionary object with property names and values towards which
+ *                     the element will transition.
+ */
+function transition(t, baseStyles, finalStyles) {
+  // Clear styles from previous test.
+  testEl.style.cssText = "";
+  testEl.className = "";
+  getComputedStyle(testEl).height;
+
+  // Set base styles
+  const defaultTransition = {
+    "transition-property": Object.keys(finalStyles).join(", "),
+    "transition-timing-function": "linear",
+    "transition-duration": "10s",
+    "transition-delay": "-5s",
+  };
+  addStyle(t, {
+    "#test": makeDeclaration(Object.assign(defaultTransition, baseStyles)),
+    "#test.transition": makeDeclaration(finalStyles),
+  });
+  getComputedStyle(testEl).height;
+
+  // Start the transition
+  testEl.className = "transition";
+}
+
+test(t => {
+  transition(t, {
+    "block-size": "0px",
+  }, {
+    "block-size": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).height, '50px');
+}, 'Logical properties can be transitioned');
+
+test(t => {
+  transition(t, {
+    "block-size": "0px",
+    "writing-mode": "vertical-rl",
+  }, {
+    "block-size": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).width, '50px');
+  assert_equals(getComputedStyle(testEl).height, '0px');
+}, 'Logical properties in transitions respect the writing-mode');
+
+test(t => {
+  transition(t, {
+    "margin-inline-start": "0px",
+    "direction": "rtl",
+  }, {
+    "margin-inline-start": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).marginLeft, '0px');
+  assert_equals(getComputedStyle(testEl).marginRight, '50px');
+}, 'Logical properties in transitions respect the direction');
+
+test(t => {
+  transition(t, {
+    "block-size": "0px",
+    "height": "200px",
+  }, {
+    "block-size": "100px",
+    "height": "300px",
+  });
+  assert_equals(getComputedStyle(testEl).height, '250px');
+}, 'Declaration order is respected within declaration blocks');
+
+test(t => {
+  transition(t, {
+    "transition-timing-function": "step-start",
+  }, {
+    "margin-top": "200px",
+    "margin-block-start": "100px"
+  });
+  assert_equals(getComputedStyle(testEl).marginTop, '100px');
+}, 'Logical properties are able to override physical properties in declaration blocks');
+
+test(t => {
+  transition(t, {
+    "transition-timing-function": "step-start",
+  }, {
+    "margin-inline": "200px",
+    "margin-inline-start": "0px",
+    "margin-inline-start": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).marginLeft, '100px');
+}, 'Declaration order is respected amongst logical properties within declaration blocks');
+
+test(t => {
+  transition(t, {
+    "block-size": "200px",
+  }, {
+    "height": "300px",
+  });
+  assert_equals(getComputedStyle(testEl).height, '250px');
+}, 'Physical properties and logical properties can be mixed');
+
+test(t => {
+  transition(t, {
+    "height": "100px",
+    "block-size": "200px",
+  }, {
+    "block-size": "100px",
+    "height": "300px",
+  });
+  assert_equals(getComputedStyle(testEl).height, '250px');
+}, 'Declaration order is respected on each keyframe individually');
+
+test(t => {
+  transition(t, {
+    "width": "0px",
+    "height": "0px",
+    "block-size": "0px",
+  }, {
+    "block-size": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).width, '0px');
+  assert_equals(getComputedStyle(testEl).height, '50px');
+
+  testEl.style.writingMode = 'vertical-rl';
+  assert_equals(getComputedStyle(testEl).width, '50px');
+  assert_equals(getComputedStyle(testEl).height, '0px');
+}, 'Transitions update when the writing-mode is changed');
+
+promise_test(async t => {
+  transition(t, {
+    "transition-delay": "-9.9s",
+    "width": "0px",
+    "height": "0px",
+    "block-size": "0px",
+  }, {
+    "block-size": "100px",
+  });
+  const watcher = new EventWatcher(t, testEl, [ 'transitionend' ]);
+  await watcher.wait_for('transitionend');
+
+  assert_equals(getComputedStyle(testEl).width, '0px');
+  assert_equals(getComputedStyle(testEl).height, '100px');
+
+  testEl.style.transition = 'none';
+  testEl.style.writingMode = 'vertical-rl';
+  assert_equals(getComputedStyle(testEl).width, '100px');
+  assert_equals(getComputedStyle(testEl).height, '0px');
+}, 'Filling transitions update when the writing-mode is changed');
+
+test(t => {
+  transition(t, {
+    "width": "0px",
+    "height": "0px",
+  }, {
+    "block-size": "100px",
+    "height": "200px",
+  });
+
+  // Initially we are interpolating the height from 0 to 200px
+  assert_equals(getComputedStyle(testEl).width, '0px');
+  assert_equals(getComputedStyle(testEl).height, '100px');
+
+  // But once we change the writing-mode, we will be interpolating *both*
+  // the height (from 0px to 200px) *and* the width (from 0px to 100px).
+  testEl.style.writingMode = 'vertical-rl';
+  assert_equals(getComputedStyle(testEl).width, '50px');
+  assert_equals(getComputedStyle(testEl).height, '100px');
+}, 'The number of interpolating properties can be increased when the'
+   + ' writing-mode is changed');
+
+test(t => {
+  transition(t, {
+    "width": "100px",
+    "height": "100px",
+  }, {
+    "width": "500px",
+    "block-size": "200px",
+  });
+
+  // Initially we are interpolating the width (100px -> 500px) and the height
+  // (100px -> 200px).
+  assert_equals(getComputedStyle(testEl).width, '300px');
+  assert_equals(getComputedStyle(testEl).height, '150px');
+
+  // Once we change the writing-mode, we will be interpolating *only* the
+  // width (300px -> 200px).
+  testEl.style.writingMode = 'vertical-rl';
+  assert_equals(getComputedStyle(testEl).width, '250px');
+  assert_equals(getComputedStyle(testEl).height, '100px');
+}, 'The number of interpolating properties can be decreased when the'
+   + ' writing-mode is changed');
+
+test(t => {
+  addStyle(t, { ':root': '--writingMode: horizontal-tb' });
+  transition(t, {
+    "width": "0px",
+    "height": "0px",
+    "writing-mode": "var(--writingMode)",
+    "block-size": "0px",
+  }, {
+    "block-size": "100px"
+  });
+  assert_equals(getComputedStyle(testEl).width, '0px');
+  assert_equals(getComputedStyle(testEl).height, '50px');
+
+  testEl.style.setProperty('--writingMode', 'vertical-rl');
+  assert_equals(getComputedStyle(testEl).width, '50px');
+  assert_equals(getComputedStyle(testEl).height, '0px');
+}, 'Transitions update when the writing-mode is changed through a CSS variable');
+
+test(t => {
+  transition(t, {
+    "margin-inline-start": "0px",
+  }, {
+    "margin-inline-start": "100px",
+  });
+  assert_equals(getComputedStyle(testEl).marginLeft, '50px');
+  assert_equals(getComputedStyle(testEl).marginRight, '0px');
+
+  testEl.style.direction = 'rtl';
+  assert_equals(getComputedStyle(testEl).marginLeft, '0px');
+  assert_equals(getComputedStyle(testEl).marginRight, '50px');
+}, 'Transitions update when the direction is changed');
+
+</script>
diff --git a/css/css-masking/idlharness.html b/css/css-masking/idlharness.html
index 41bab66..c415eaa 100644
--- a/css/css-masking/idlharness.html
+++ b/css/css-masking/idlharness.html
@@ -17,8 +17,7 @@
         SVGClipPathElement: [document.querySelector('#clip1')],
         SVGMaskElement: [document.querySelector('#mask1')],
       });
-    },
-    'Test IDL implementation of css-masking'
+    }
   );
 </script>
 
diff --git a/css/css-parser-api/idlharness.html b/css/css-parser-api/idlharness.html
index 79b6079..078a108 100644
--- a/css/css-parser-api/idlharness.html
+++ b/css/css-parser-api/idlharness.html
@@ -13,7 +13,6 @@
     ['cssom'],
     idl_array => {
       // No objects
-    },
-    'CSS-Parser-API interfaces'
+    }
   );
 </script>
diff --git a/css/css-properties-values-api/typedom.tentative.html b/css/css-properties-values-api/typedom.tentative.html
index 9fff080..0652806 100644
--- a/css/css-properties-values-api/typedom.tentative.html
+++ b/css/css-properties-values-api/typedom.tentative.html
@@ -13,16 +13,22 @@
 // Properties are generated on demand, as `--prop-${g_counter}`.
 let g_counter = 1;
 
+// Generate a new property name.
+function gen_name() {
+    let name = `--prop-${g_counter}`;
+    g_counter++;
+    return name;
+}
+
 // Generate a property and return its name.
 function gen_prop(syntax, initialValue) {
-    let name = `--prop-${g_counter}`;
+    let name = gen_name();
     CSS.registerProperty({
         name: name,
         syntax: syntax,
         initialValue: initialValue,
         inherits: false
     });
-    g_counter++;
     return name;
 }
 
@@ -50,6 +56,28 @@
     }
 }
 
+function assert_attribute_get_type(syntax, value, expected) {
+    let name = gen_name();
+    target.style = `${name}: ${value}`;
+
+    assert_true(target.attributeStyleMap.get(name) instanceof CSSUnparsedValue);
+
+    CSS.registerProperty({
+        name: name,
+        syntax: syntax,
+        initialValue: value,
+        inherits: false
+    });
+
+    if (expected == CSSStyleValue) {
+        assert_false(target.attributeStyleMap.get(name) instanceof CSSUnparsedValue);
+    }
+
+    assert_true(target.attributeStyleMap.get(name) instanceof expected);
+}
+
+// computedStyleMap
+
 test(function(){
     let name = gen_prop('*', 'if(){}');
     assert_true(target.computedStyleMap().get(name) instanceof CSSUnparsedValue);
@@ -164,4 +192,110 @@
     assert_true(target.computedStyleMap().getAll(name).every(x => x instanceof CSSUnitValue));
 }, 'All computed values correctly reified in comma-separated list');
 
+// attributeStyleMap.get
+
+test(function(){
+    let name1 = gen_prop('<length>', '100px');
+    let name2 = gen_prop('<length>', '0px');
+    target.style = `${name2}: var(${name1})`;
+    assert_true(target.attributeStyleMap.get(name2) instanceof CSSUnparsedValue);
+}, 'attributeStyleMap.get returns CSSUnparsedValue for value with var references');
+
+test(function(){
+    let name1 = gen_prop('<length>', '100px');
+    let name2 = gen_prop('<length>#', '0px');
+    target.style = `${name2}: 1px, var(${name1}), 3px`;
+    assert_true(target.attributeStyleMap.get(name2) instanceof CSSUnparsedValue);
+}, 'attributeStyleMap.get returns CSSUnparsedValue for value with var reference in list');
+
+test(function(){
+    assert_attribute_get_type('*', 'if(){}', CSSUnparsedValue);
+}, 'attributeStyleMap.get returns CSSUnparsedValue for *');
+
+test(function(){
+    assert_attribute_get_type('<angle>', '42deg', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <angle>');
+
+test(function(){
+    assert_attribute_get_type('<color>', '#fefefe', CSSStyleValue);
+}, 'attributeStyleMap.get returns CSSStyleValue for <color>');
+
+test(function(){
+    assert_attribute_get_type('<custom-ident>', 'none', CSSKeywordValue);
+}, 'attributeStyleMap.get returns CSSKeywordValue for <custom-ident>');
+
+test(function(){
+    assert_attribute_get_type('<image>', 'url(thing.png)', CSSImageValue);
+}, 'attributeStyleMap.get returns CSSImageValue for <image>');
+
+test(function(){
+    assert_attribute_get_type('<integer>', '100', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <integer>');
+
+test(function(){
+    assert_attribute_get_type('<length-percentage>', '10%', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <length-percentage> [10%]');
+
+test(function(){
+    assert_attribute_get_type('<length-percentage>', '10px', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <length-percentage> [10px]');
+
+test(function(){
+    assert_attribute_get_type('<length-percentage>', 'calc(10px + 10%)', CSSMathSum);
+}, 'attributeStyleMap.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)]');
+
+test(function(){
+    assert_attribute_get_type('<length>', '10px', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <length>');
+
+test(function(){
+    assert_attribute_get_type('<number>', '42', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <number>');
+
+test(function(){
+    assert_attribute_get_type('<percentage>', '10%', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <percentage>');
+
+test(function(){
+    assert_attribute_get_type('<resolution>', '300dpi', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <resolution>');
+
+test(function(){
+    assert_attribute_get_type('<time>', '42s', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <time>');
+
+test(function(){
+    assert_attribute_get_type('<url>', 'url(a)', CSSStyleValue);
+}, 'attributeStyleMap.get returns CSSStyleValue for <url>');
+
+test(function(){
+    assert_attribute_get_type('thing1 | THING2', 'thing1', CSSKeywordValue);
+}, 'attributeStyleMap.get returns CSSKeywordValue for thing1 | THING2');
+
+test(function(){
+    assert_attribute_get_type('<length>+', '10px 20px', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <length>+');
+
+test(function(){
+    assert_attribute_get_type('<length>#', '10px 20px', CSSUnitValue);
+}, 'attributeStyleMap.get returns CSSUnitValue for <length>#');
+
+// attributeStyleMap.getAll
+
+test(function(){
+    let name = gen_prop('<length>+', '0px');
+    target.attributeStyleMap.clear();
+    target.attributeStyleMap.set(name, '10px 20px 30px');
+    assert_equals(target.attributeStyleMap.getAll(name).length, 3);
+    assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue));
+}, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>+');
+
+test(function(){
+    let name = gen_prop('<length>#', '0px');
+    target.attributeStyleMap.clear();
+    target.attributeStyleMap.set(name, '10px, 20px, 30px');
+    assert_equals(target.attributeStyleMap.getAll(name).length, 3);
+    assert_true(target.attributeStyleMap.getAll(name).every(x => x instanceof CSSUnitValue));
+}, 'attributeStyleMap.getAll returns a list of CSSUnitValues for <length>#');
+
 </script>
diff --git a/css/css-properties-values-api/var-reference-registered-properties.html b/css/css-properties-values-api/var-reference-registered-properties.html
index 77ae299..d8a8315 100644
--- a/css/css-properties-values-api/var-reference-registered-properties.html
+++ b/css/css-properties-values-api/var-reference-registered-properties.html
@@ -22,6 +22,10 @@
     --registered-token-stream-1:var(--invalid);
     --registered-token-stream-2:var(--invalid,fallback);
     --token-stream-1:var(--registered-token-stream-1,moo);
+
+    --registered-length-list-1: 1px, var(--registered-length-1), 2px;
+    --registered-length-list-2: 1px, var(--length-1), var(--registered-length-1), 2px;
+    --registered-length-list-3: var(--registered-length-list-1), var(--registered-length-list-2);
 }
 </style>
 <div id=element></div>
@@ -58,4 +62,39 @@
     assert_equals(computedStyle.getPropertyValue('--registered-token-stream-2'), 'fallback');
     assert_equals(computedStyle.getPropertyValue('--token-stream-1'), 'moo');
 }, "var() references work with registered properties");
+
+test(function(){
+    CSS.registerProperty({
+        name: '--registered-length-list-1',
+        syntax: '<length>#',
+        initialValue: '0px',
+        inherits: false
+    });
+    let computedStyle = getComputedStyle(element);
+    assert_equals(computedStyle.getPropertyValue('--registered-length-list-1'), '1px, 10px, 2px');
+}, 'References to registered var()-properties work in registered lists');
+
+test(function(){
+    CSS.registerProperty({
+        name: '--registered-length-list-2',
+        syntax: '<length>#',
+        initialValue: '0px',
+        inherits: false
+    });
+    let computedStyle = getComputedStyle(element);
+    assert_equals(computedStyle.getPropertyValue('--registered-length-list-2'), '1px, 20px, 10px, 2px');
+}, 'References to mixed registered and unregistered var()-properties work in registered lists');
+
+test(function(){
+    CSS.registerProperty({
+        name: '--registered-length-list-3',
+        syntax: '<length>#',
+        initialValue: '0px',
+        inherits: false
+    });
+    let computedStyle = getComputedStyle(element);
+    assert_equals(computedStyle.getPropertyValue('--registered-length-list-3'), '1px, 10px, 2px, 1px, 20px, 10px, 2px');
+}, 'Registered lists may be concatenated');
+
+
 </script>
diff --git a/css/css-regions/idlharness.html b/css/css-regions/idlharness.html
index 7f56e18..3d6f55c 100644
--- a/css/css-regions/idlharness.html
+++ b/css/css-regions/idlharness.html
@@ -15,7 +15,6 @@
       idl_array.add_objects({
         Document: ['document'],
       })
-    },
-    'css-regions interfaces'
+    }
   );
 </script>
diff --git a/css/css-transitions/idlharness.html b/css/css-transitions/idlharness.html
index 6856952..4cc7ee5 100644
--- a/css/css-transitions/idlharness.html
+++ b/css/css-transitions/idlharness.html
@@ -19,7 +19,6 @@
         HTMLElement: ['document'],
         Window: ['window'],
       })
-    },
-    'Test IDL implementation of css-transitions API'
+    }
   );
 </script>
diff --git a/css/css-typed-om/interfaces.html b/css/css-typed-om/interfaces.html
index d21f3f4..976bb32 100644
--- a/css/css-typed-om/interfaces.html
+++ b/css/css-typed-om/interfaces.html
@@ -72,7 +72,6 @@
       CSSPerspective: ['perspective'],
       CSSMatrixComponent: ['matrix'],
     });
-  },
-  'CSS Typed OM IDL test'
+  }
 );
 </script>
diff --git a/css/cssom-view/interfaces.html b/css/cssom-view/interfaces.html
index c545e32..d3de0cf 100644
--- a/css/cssom-view/interfaces.html
+++ b/css/cssom-view/interfaces.html
@@ -40,7 +40,6 @@
     });
 
     await waitForLoad;
-  },
-  'cssom-view interfaces'
+  }
 );
 </script>
diff --git a/css/cssom/interfaces.html b/css/cssom/interfaces.html
index d409e48..3204fb8 100644
--- a/css/cssom/interfaces.html
+++ b/css/cssom/interfaces.html
@@ -70,8 +70,7 @@
     self.sheet = style_element.sheet;
     self.svg_element = document.getElementById('svgElement');
     self.xmlss_pi = document.getElementById('xmlssPiIframe').contentDocument.firstChild;
-  },
-  'Test driver'
+  }
 );
 
 </script>
diff --git a/css/filter-effects/interfaces.any.js b/css/filter-effects/interfaces.any.js
index 88fab2f..e7de16e 100644
--- a/css/filter-effects/interfaces.any.js
+++ b/css/filter-effects/interfaces.any.js
@@ -10,6 +10,5 @@
   ['SVG', 'html', 'dom'],
   idl_array => {
     // TODO: objects
-  },
-  'Filter effects interfaces.'
+  }
 );
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-absolute-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-absolute-001.html
new file mode 100644
index 0000000..036cf7d
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-absolute-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' element should contain absolute position elements.</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+  <link rel="match" href="contain-paint-containing-block-absolute-001-ref.html">
+  <style>
+  #a {
+      contain: layout;
+      width: 100px;
+      height: 100px;
+      background: red;
+      margin: 50px;
+  }
+  #b {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-fixed-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-fixed-001.html
new file mode 100644
index 0000000..979d71d
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-containing-block-fixed-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' element should contain fixed position elements.</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+  <link rel="match" href="contain-paint-containing-block-fixed-001-ref.html">
+  <style>
+  #a {
+      contain: layout;
+      width: 100px;
+      height: 100px;
+      background: red;
+      margin: 50px;
+  }
+  #b {
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-float-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-float-001.html
new file mode 100644
index 0000000..fe491aa
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-float-001.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should contain floats as a formatting context.</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="http://www.w3.org/TR/css-containment-1/#containment-layout">
+  <link rel="match" href="contain-paint-formatting-context-float-001-ref.html">
+  <style>
+  #left {
+    float: left;
+    height: 50px;
+    width: 10px;
+    background: blue;
+  }
+  #a {
+    contain: layout;
+    background: red;
+    margin: 10px;
+    width: 50px;
+    height: 50px;
+  }
+  #b {
+    clear: left;
+    width: 50px;
+    height: 50px;
+    background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="left"></div>
+  <div id="a">
+    <div id="b"></div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001-ref.html
new file mode 100644
index 0000000..5a6d653
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Test</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  #a {
+      background: blue;
+      margin: 10px;
+      width: 50px;
+      height: 50px;
+  }
+  #b {
+      width: 50px;
+      height: 40px;
+      background: green;
+  }
+  #b-padding {
+      height: 10px;
+  }
+  #c {
+    width: 50px;
+    height: 10px;
+    background: lightblue;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div id="b-padding"></div>
+    <div id="b"></div>
+    <div id="c"></div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001.html
new file mode 100644
index 0000000..c346a52
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-formatting-context-margin-001.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' with a vertical margin child. Margin collapse should not occur, and neither should overflow clipping.</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="http://www.w3.org/TR/css-containment-1/#containment-layout">
+  <link rel="match" href="contain-paint-formatting-context-margin-001-ref.html">
+  <style>
+  #a {
+      contain:layout;
+      background: blue;
+      margin: 10px;
+      width: 50px;
+      height: 50px;
+  }
+  #b {
+      width: 50px;
+      height: 40px;
+      background: green;
+      margin-top: 10px;
+  }
+  #c {
+      background: lightblue;
+      width: 50px;
+      height: 10px;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div id="b"></div>
+    <div id="c"></div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-001.html
new file mode 100644
index 0000000..bd2f4cb
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-001.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should not create a stacking context when no principle box is generated.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-paint-ignored-cases-no-principal-box-001-ref.html">
+  <style>
+    div {
+      position: relative;
+      width: 100px;
+    }
+    #div1,
+    #div3 {
+      background-color: #cfc;
+      height: 100px;
+    }
+    #div1 {
+      z-index: 5;
+    }
+    #div2 {
+      display: contents;
+      contain: layout;
+      background-color: #fdd;
+      height: 100px;
+      top: -20px;
+    }
+    #div2_1 {
+      background-color: #ffc;
+      z-index: 6;
+      top: -10px;
+      height: 100px;
+    }
+    #div2_2 {
+      z-index: 3;
+      position: absolute;
+      top: -15px;
+      width: 40px;
+      height: 300px;
+      background-color: #ddf;
+    }
+    #div3 {
+      z-index: 2;
+      top: -50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="div1"></div>
+
+  <div id="div2">
+    <div id="div2_1"></div>
+
+    <div id="div2_2"></div>
+  </div>
+
+  <div id="div3"></div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002-ref.html
new file mode 100644
index 0000000..44cd7c1
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  #a {
+      display: contents;
+      width: 100px;
+      height: 100px;
+      background: green;
+      margin: 50px;
+  }
+ #b {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002.html
new file mode 100644
index 0000000..de2edfb
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-002.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' element should not contain absolute/fixed position elements when no principal box is generated.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+  <link rel="match" href="contain-layout-ignored-cases-no-principal-box-002-ref.html">
+  <style>
+  #a {
+      contain: layout;
+      display: contents;
+      width: 100px;
+      height: 100px;
+      background: red;
+      margin: 50px;
+  }
+  #b {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100px;
+      height: 100px;
+      background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003-ref.html
new file mode 100644
index 0000000..46f028c
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  #a {
+    display: contents;
+    width: 100px;
+    height: 100px;
+    background: red;
+    margin: 50px;
+  }
+  #b {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100px;
+    height: 100px;
+    background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003.html
new file mode 100644
index 0000000..d40a021
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-ignored-cases-no-principal-box-003.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' element should not contain absolute/fixed position elements when no principal box is generated.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+  <link rel="match" href="contain-layout-ignored-cases-no-principal-box-003-ref.html">
+  <style>
+  #a {
+    contain: layout;
+    display: contents;
+    width: 100px;
+    height: 100px;
+    background: red;
+    margin: 50px;
+  }
+  #b {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100px;
+    height: 100px;
+    background: green;
+  }
+  </style>
+</head>
+<body>
+  <div id="a">
+    <div>
+      <div id="b"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001-ref.html
new file mode 100644
index 0000000..400369b
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg-1 {
+    height: 95px;
+    width: 95px;
+    background: lightblue;
+  }
+  .inner-lg-2 {
+    height: 200px;
+    width: 200px;
+  }
+  .pass {
+    background: green;
+  }
+  .border {
+    border: 5px solid green;
+  }
+
+  </style>
+</head>
+<body>
+  <div class="outer">
+    <div class="inner-sm"></div>
+  </div>
+  <br>
+
+  <div class="outer auto">
+    <div class="inner-lg-2 pass">
+    </div>
+  </div>
+  <br>
+
+  <div class="inner-sm border">
+    <div class="inner-lg-1">
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001.html
new file mode 100644
index 0000000..e4330ec
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-001.html
@@ -0,0 +1,71 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should force all overflow to be ink overflow.</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-layout-overflow-001-ref.html">
+  <style>
+  .contain {
+    contain: layout;
+  }
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg {
+    height: 200px;
+    width: 200px;
+    background: lightblue;
+  }
+  .pass {
+    background: green;
+  }
+  .fail {
+    background: red;
+  }
+  .border {
+    border: 5px solid green;
+  }
+  </style>
+</head>
+<body>
+  <!--CSS Test: Elements with contain:layout that do not produce scrollable overflow should paint as if containment were not applied. -->
+  <div class="outer">
+    <div class="inner-sm contain"></div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that overflow their container and have children who overflow should produce the same amount of scrollable overflow as if there were no children. -->
+  <div class="outer auto">
+    <div class="inner-lg contain">
+      <div class="inner-lg pass"></div>
+      <div class="inner-lg fail"></div>
+    </div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that do not overflow their container, but have children who overflow, should not allow their children to affect the scrollable overflow regions of their parent. -->
+  <div class="outer auto">
+    <div class="inner-sm contain border">
+      <div class="inner-lg">
+      </div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002-ref.html
new file mode 100644
index 0000000..e35eb9b
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <style>
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg-1 {
+    height: 95px;
+    width: 95px;
+    float:left;
+    background: lightblue;
+  }
+  .inner-lg-2 {
+    height: 200px;
+    width: 200px;
+    float:left;
+  }
+  .pass {
+    background: green;
+  }
+  .border {
+    border: 5px solid green;
+  }
+
+  </style>
+</head>
+<body>
+  <div class="outer">
+    <div class="inner-sm" style="float:left;"></div>
+  </div>
+  <br>
+
+  <div class="outer auto">
+    <div class="inner-lg-2 pass">
+    </div>
+  </div>
+  <br>
+
+  <div class="inner-sm border">
+    <div class="inner-lg-1">
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002.html
new file mode 100644
index 0000000..aa02bcd
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-overflow-002.html
@@ -0,0 +1,73 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' should force all overflow to be ink overflow.</title>
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-layout-overflow-002-ref.html">
+  <style>
+  .contain {
+    contain: layout;
+  }
+  .outer {
+    height: 100px;
+    width: 100px;
+  }
+  .auto {
+    overflow: auto;
+  }
+  .inner-sm {
+    height: 50px;
+    width: 50px;
+    background: lightblue;
+  }
+  .inner-md {
+    height: 100px;
+    width: 100px;
+    background: lightblue;
+  }
+  .inner-lg {
+    height: 200px;
+    width: 200px;
+    background: lightblue;
+    float: left;
+  }
+  .pass {
+    background: green;
+  }
+  .fail {
+    background: red;
+  }
+  .border {
+    border: 5px solid green;
+  }
+  </style>
+</head>
+<body>
+  <!--CSS Test: Elements with contain:layout that do not produce scrollable overflow should paint as if containment were not applied. -->
+  <div class="outer">
+    <div class="inner-sm contain" style="float:left;"></div>
+  </div>
+  <br>
+
+  <!--CSS Test: Layout-contained elements that overflow their container and have children who overflow should produce the same amount of scrollable overflow as if there were no children. -->
+  <div class="outer auto">
+    <div class="outer contain">
+      <div class="inner-lg pass"></div>
+      <div class="inner-lg fail"></div>
+    </div>
+  </div>
+  <br>
+
+
+  <!--CSS Test: Layout-contained elements that do not overflow their container, but have children who overflow, should not allow their children to affect the scrollable overflow regions of their parent. -->
+  <div class="outer auto">
+    <div class="inner-sm contain border">
+      <div class="inner-lg">
+      </div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-stacking-context-001.html b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-stacking-context-001.html
new file mode 100644
index 0000000..4ec3bce
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-layout-stacking-context-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: layout' with stacking contents. Z-index is defined only for siblings and children.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+  <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
+  <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel="match" href="contain-paint-stacking-context-001-ref.html">
+  <style>
+    div {
+      position: relative;
+      width: 100px;
+    }
+    #div1,
+    #div3 {
+      background-color: #cfc;
+    }
+    #div1 {
+      z-index: 5;
+    }
+    #div2 {
+      contain: layout;
+      background-color: #fdd;
+      height: 100px;
+      top: -20px;
+    }
+    #div2_1 {
+      background-color: #ffc;
+      z-index: 6;
+      top: -10px;
+    }
+    #div2_2 {
+      z-index: 3;
+      position: absolute;
+      top: -15px;
+      width: 40px;
+      height: 100px;
+      background-color: #ddf;
+    }
+    #div3 {
+      z-index: 2;
+      top: -50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="div1">
+    <br/><br/>
+  </div>
+
+  <div id="div2">
+    <div id="div2_1">
+      <br/><br/>
+    </div>
+
+    <div id="div2_2">
+    </div>
+  </div>
+
+  <div id="div3">
+    <br/><br/>
+  </div>
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/reftest.list b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/reftest.list
index 83e3f0c..3760a5b 100644
--- a/css/vendor-imports/mozilla/mozilla-central-reftests/contain/reftest.list
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/contain/reftest.list
@@ -26,4 +26,14 @@
 == contain-size-fieldset-002.html contain-size-fieldset-002-ref.html
 == contain-size-multicol-002.html contain-size-multicol-002-ref.html
 == contain-size-multicol-003.html contain-size-multicol-003-ref.html
+== contain-layout-overflow-001.html contain-layout-overflow-001-ref.html
+== contain-layout-overflow-002.html contain-layout-overflow-002-ref.html
 == contain-size-table-caption-001.html contain-size-table-caption-001-ref.html
+== contain-layout-stacking-context-001.html contain-paint-stacking-context-001-ref.html
+== contain-layout-formatting-context-float-001.html contain-paint-formatting-context-float-001-ref.html
+== contain-layout-formatting-context-margin-001.html contain-layout-formatting-context-margin-001-ref.html
+== contain-layout-containing-block-fixed-001.html contain-paint-containing-block-fixed-001-ref.html
+== contain-layout-containing-block-absolute-001.html contain-paint-containing-block-absolute-001-ref.html
+== contain-layout-ignored-cases-no-principal-box-001.html contain-paint-ignored-cases-no-principal-box-001-ref.html
+== contain-layout-ignored-cases-no-principal-box-002.html contain-layout-ignored-cases-no-principal-box-002-ref.html
+== contain-layout-ignored-cases-no-principal-box-003.html contain-layout-ignored-cases-no-principal-box-003-ref.html
diff --git a/dom/nodes/attributes.html b/dom/nodes/attributes.html
index 2118893..dac191b 100644
--- a/dom/nodes/attributes.html
+++ b/dom/nodes/attributes.html
@@ -604,7 +604,7 @@
 test(function() {
   var el = document.createElement("div");
   var other = document.createElement("div");
-  attr = document.createAttribute("foo");
+  var attr = document.createAttribute("foo");
   assert_equals(el.setAttributeNode(attr), null);
   assert_equals(attr.ownerElement, el);
   assert_throws("INUSE_ATTRIBUTE_ERR",
@@ -614,7 +614,7 @@
 
 test(function() {
   var el = document.createElement("div");
-  attr = document.createAttribute("foo");
+  var attr = document.createAttribute("foo");
   assert_equals(el.setAttributeNode(attr), null);
   el.setAttribute("bar", "qux");
   assert_equals(el.setAttributeNode(attr), attr);
diff --git a/domparsing/interfaces.any.js b/domparsing/interfaces.any.js
index f01aff0..870c165 100644
--- a/domparsing/interfaces.any.js
+++ b/domparsing/interfaces.any.js
@@ -15,5 +15,5 @@
       Range: ['new Range()'],
       XMLSerializer: ['new XMLSerializer()'],
     })
-  },
-  'DOM-Parsing interfaces');
+  }
+);
diff --git a/encoding/remove-only-one-bom.html b/encoding/remove-only-one-bom.html
new file mode 100644
index 0000000..8b91f7f
--- /dev/null
+++ b/encoding/remove-only-one-bom.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+setup({explicit_done:true});
+function runTests() {
+    for (var i = 0; i < window.frames.length; i++) {
+        test(function() {
+            assert_equals(window.frames[i].window.document.body.textContent, "\uFEFF");
+        }, "Should have removed only one BOM from frame " + i);
+    }
+    done();
+}
+</script>
+<body onload="runTests()">
+<iframe src="resources/two-boms-utf-8.html"></iframe>
+<iframe src="resources/two-boms-utf-16le.html"></iframe>
+<iframe src="resources/two-boms-utf-16be.html"></iframe>
diff --git a/encoding/resources/two-boms-utf-16be.html b/encoding/resources/two-boms-utf-16be.html
new file mode 100644
index 0000000..b8c055b
--- /dev/null
+++ b/encoding/resources/two-boms-utf-16be.html
@@ -0,0 +1 @@
+í³¾í³¿í³¾í³¿
\ No newline at end of file
diff --git a/encoding/resources/two-boms-utf-16le.html b/encoding/resources/two-boms-utf-16le.html
new file mode 100644
index 0000000..8042f33
--- /dev/null
+++ b/encoding/resources/two-boms-utf-16le.html
@@ -0,0 +1 @@
+í³¿í³¾í³¿í³¾
\ No newline at end of file
diff --git a/encoding/resources/two-boms-utf-8.html b/encoding/resources/two-boms-utf-8.html
new file mode 100644
index 0000000..83ea941
--- /dev/null
+++ b/encoding/resources/two-boms-utf-8.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/fetch/api/idl.any.js b/fetch/api/idl.any.js
index a3dec79..ccd4d2c 100644
--- a/fetch/api/idl.any.js
+++ b/fetch/api/idl.any.js
@@ -16,6 +16,5 @@
     } else if (self.GLOBAL.isWorker()) {
       idl_array.add_objects({ WorkerGlobalScope: ['self'] });
     }
-  },
-  'Fetch Standard IDL'
+  }
 );
diff --git a/fetch/cors-rfc1918/idlharness.tentative.any.js b/fetch/cors-rfc1918/idlharness.tentative.any.js
index b3b6f2b..88468dc 100644
--- a/fetch/cors-rfc1918/idlharness.tentative.any.js
+++ b/fetch/cors-rfc1918/idlharness.tentative.any.js
@@ -19,6 +19,5 @@
         Document: ['document'],
       });
     }
-  },
-  'Test CORS RFC1918 interfaces'
+  }
 );
diff --git a/gamepad/idlharness-manual.html b/gamepad/idlharness-manual.html
index c04b911..2964ee5 100644
--- a/gamepad/idlharness-manual.html
+++ b/gamepad/idlharness-manual.html
@@ -32,7 +32,6 @@
       t.step_timeout(() => { reject('Timed out waiting for gamepad'); }, 5000);
     });
     return Promise.race([gamepadConnected, timeout]);
-  },
-  'Test IDL implementation of Gamepad API'
+  }
 );
 </script>
diff --git a/gamepad/idlharness.window.js b/gamepad/idlharness.window.js
index e2c0e2b..7943520 100644
--- a/gamepad/idlharness.window.js
+++ b/gamepad/idlharness.window.js
@@ -13,6 +13,5 @@
       GamepadEvent: ['new GamepadEvent("gamepad")'],
       Navigator: ['navigator']
     });
-  },
-  'Test IDL implementation of Gamepad API'
+  }
 );
diff --git a/generic-sensor/idlharness.https.window.js b/generic-sensor/idlharness.https.window.js
index 8da0307..f591d75 100644
--- a/generic-sensor/idlharness.https.window.js
+++ b/generic-sensor/idlharness.https.window.js
@@ -19,5 +19,5 @@
         'new SensorErrorEvent("error", { error: new DOMException });'
       ],
     });
-  },
-  'Test IDL implementation of Generic Sensor');
+  }
+);
diff --git a/geolocation-sensor/idlharness.https.window.js b/geolocation-sensor/idlharness.https.window.js
index eb3da34..d843dc6 100644
--- a/geolocation-sensor/idlharness.https.window.js
+++ b/geolocation-sensor/idlharness.https.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       GeolocationSensor: ['new GeolocationSensor'],
     });
-  },
-  'Test IDL implementation of Geolocation Sensor'
+  }
 );
diff --git a/gyroscope/idlharness.https.window.js b/gyroscope/idlharness.https.window.js
index dfdac4e..00f6696 100644
--- a/gyroscope/idlharness.https.window.js
+++ b/gyroscope/idlharness.https.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       Gyroscope: ['new Gyroscope();']
     });
-  },
-  'Test IDL implementation of Gyroscope Sensor'
+  }
 );
diff --git a/html-media-capture/idlharness.window.js b/html-media-capture/idlharness.window.js
index 5382f53..50faf83 100644
--- a/html-media-capture/idlharness.window.js
+++ b/html-media-capture/idlharness.window.js
@@ -14,5 +14,5 @@
     });
 
     self.input = document.createElement('input');
-  },
-  'html-media-capture interfaces.');
+  }
+);
diff --git a/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html b/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html
new file mode 100644
index 0000000..6806eaf
--- /dev/null
+++ b/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>beforeunload event is emitted synchronously</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+'use strict';
+// "navigate a browsing context" synchronously calls "prompt to unload", which
+// synchronously calls "dispatch an event".
+
+async_test(function(t) {
+  var iframe = document.createElement('iframe');
+
+  iframe.onload = t.step_func(function() {
+    var callCount = 0;
+
+    iframe.contentWindow.onbeforeunload = function() {
+      callCount += 1;
+    };
+
+    iframe.contentWindow.location.href = '/common/blank.html';
+
+    assert_equals(callCount, 1, 'invoked synchronously exactly once');
+
+    t.done();
+  });
+
+  document.body.appendChild(iframe);
+});
+</script>
+</body>
diff --git a/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html b/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html
new file mode 100644
index 0000000..85c52e0
--- /dev/null
+++ b/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Auxiliary Browing Contexts: window.opener setter</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/PrefixedLocalStorage.js"></script>
+  </head>
+  <body>
+    <div id="log"></div>
+    <script>
+    var prefixedLocalStorage;
+    setup(() => prefixedLocalStorage = new PrefixedLocalStorageTest());
+    async_test(t => {
+      t.add_cleanup(() => prefixedLocalStorage.cleanup());
+      prefixedLocalStorage.onSet('openerIsNull', t.step_func_done(e => {
+        assert_equals(e.newValue, 'true');
+      }));
+      window.open(prefixedLocalStorage.url('resources/opener-setter.html'),
+        'iShouldSetOpenerToNull');
+    }, 'Auxiliary browsing context created via `window.open` and setting `window.opener` to `null` should report `window.opener` `null`');
+    async_test(t => {
+      t.add_cleanup(() => prefixedLocalStorage.cleanup());
+      prefixedLocalStorage.onSet('openerIsTest', t.step_func_done(e => {
+        assert_equals(e.newValue, 'true');
+      }));
+      window.open(prefixedLocalStorage.url('resources/opener-setter.html'),
+        'iShouldSetOpenerToTest');
+    }, 'Auxiliary browsing context created via `window.open` and setting `window.opener` to `test` should report `test`');
+    </script>
+  </body>
+</html>
diff --git a/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html b/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html
new file mode 100644
index 0000000..4112dae
--- /dev/null
+++ b/html/browsers/windows/auxiliary-browsing-contexts/resources/opener-setter.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<p>This window should set the window.opener attribute</p>
+<script src="/common/PrefixedLocalStorage.js"></script>
+<script>
+var prefixedLocalStorage = new PrefixedLocalStorageResource({
+  close_on_cleanup: true
+});
+function checkOpener () {
+  if (window.name == 'iShouldSetOpenerToNull') {
+    window.opener = null;
+    return prefixedLocalStorage.setItem('openerIsNull', window.opener === null);
+  }
+  if (window.name == 'iShouldSetOpenerToTest') {
+    window.opener = 'test';
+    return prefixedLocalStorage.setItem('openerIsTest', window.opener === "test");
+  }
+}
+</script>
+<body onload="checkOpener()">
+</body>
+</html>
diff --git a/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html b/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html
index 921f10a..aa27d2f 100644
--- a/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html
+++ b/html/dom/elements/global-attributes/dir_auto-contained-script-R-ref.html
@@ -28,6 +28,7 @@
         display: none;
       }
     </style>
+    <script>var x;</script>
   </head>
   <body>
     <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
diff --git a/html/dom/elements/global-attributes/dir_auto-contained-script-R.html b/html/dom/elements/global-attributes/dir_auto-contained-script-R.html
index a76515d..ee00276 100644
--- a/html/dom/elements/global-attributes/dir_auto-contained-script-R.html
+++ b/html/dom/elements/global-attributes/dir_auto-contained-script-R.html
@@ -29,6 +29,7 @@
         display: none;
       }
     </style>
+    <script>var x;</script>
   </head>
   <body>
     <div class="instructions"><p>Test passes if the two boxes below look exactly the same.</p></div>
diff --git a/html/dom/historical.html b/html/dom/historical.html
index a36b4b2..b79d35f 100644
--- a/html/dom/historical.html
+++ b/html/dom/historical.html
@@ -30,4 +30,13 @@
 test(() => {
   assert_equals(self.getComputedStyle(document.getElementsByTagName("applet")[0], "").cssFloat, "none");
 }, "applet is not styled")
+
+// removed in https://github.com/whatwg/html/commit/e383ae23776362cafb2fb4bbba70c8c9080d4b0f
+test(() => {
+  assert_false("HTMLTableDataCellElement" in window);
+}, "HTMLTableDataCellElement interface is removed")
+
+test(() => {
+  assert_false("HTMLTableHeaderCellElement" in window);
+}, "HTMLTableHeaderCellElement interface is removed")
 </script>
diff --git a/html/dom/interfaces.https.html b/html/dom/interfaces.https.html
index 78f7d13..6b7e37b 100644
--- a/html/dom/interfaces.https.html
+++ b/html/dom/interfaces.https.html
@@ -216,8 +216,7 @@
     });
     idlArray.prevent_multiple_testing('HTMLElement');
     await waitForLoad;
-  },
-  'html interfaces'
+  }
 );
 
 </script>
diff --git a/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html b/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html
new file mode 100644
index 0000000..82aa08d
--- /dev/null
+++ b/html/rendering/the-css-user-agent-style-sheet-and-presentational-hints/no-help-cursor-on-links.historical.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>link with rel="help" cursor tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<link rel="help" href="https://html.spec.whatwg.org/#phrasing-content-3">
+<link rel="help" href="https://github.com/whatwg/html/pull/3902">
+
+<div id="log"></div>
+
+<a href="/common/blank.html?unvisited" rel="help" id="unvisited">unvisited</a>
+<a href="/common/blank.html?willbevisited" rel="help" id="willbevisited">will be visited</a>
+
+<script>
+"use strict";
+
+
+test(() => {
+  const el = document.querySelector("#unvisited");
+  const style = window.getComputedStyle(el);
+
+  assert_equals(style.cursor, "pointer");
+},"Unvisited help links must have pointer cursor, not help cursor");
+
+
+// This test is kind of dubious. Browsers don't allow you to distinguish visited and unvisited links
+// from script, for privacy reasons. So we can't really be sure that loading the iframe would make
+// the link count as visited. Manually running this test turns the link purple in some browsers,
+// but leaves it blue in others. Even then it's not clear whether it turned purple before or after
+// the onload; this test assumes that once the iframe onload fires, it counts as visited, which
+// may not be justified even in the purple-turning browsers.
+//
+// Still, the test doesn't really hurt. At worst it's redundant with the above.
+//
+// If someone comes up with a better way of testing this (i.e. something that truly guarantees that
+// the link will count as "visited" for UA stylesheet purposes), then please submit a PR.
+async_test(t => {
+  const el = document.querySelector("#willbevisited");
+
+  const iframe = document.createElement("iframe");
+  iframe.src = el.href;
+  iframe.onload = t.step_func_done(() => {
+    const style = window.getComputedStyle(el);
+    assert_equals(style.cursor, "pointer");
+  });
+
+  document.body.appendChild(iframe);
+}, "Visited help links must have pointer cursor, not help cursor");
+</script>
diff --git a/html/semantics/forms/the-select-element/select-selectedOptions.html b/html/semantics/forms/the-select-element/select-selectedOptions.html
index f8e577a..6a5c75f 100644
--- a/html/semantics/forms/the-select-element/select-selectedOptions.html
+++ b/html/semantics/forms/the-select-element/select-selectedOptions.html
@@ -114,12 +114,15 @@
 test(() => {
   const select = document.getElementById("select-same-object-change");
   const before = select.selectedOptions;
+  assert_equals(before.length, 3);
 
   select.selectedOptions[1].selected = false;
 
   const after = select.selectedOptions;
 
   assert_equals(before, after);
+  assert_equals(before.length, 2);
+  assert_equals(after.length, 2);
 
 }, ".selectedOptions should return the same object after selection changes - [SameObject]");
 </script>
diff --git a/html/syntax/parsing/meta-inhead-insertion-mode.html b/html/syntax/parsing/meta-inhead-insertion-mode.html
new file mode 100644
index 0000000..4317e4e
--- /dev/null
+++ b/html/syntax/parsing/meta-inhead-insertion-mode.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Encoding specified in the "charset" attribute should have precedence over "content" attribute.</title>
+<meta http-equiv="Content-Type" content="text/html; charset=koi8-r" charset="iso-8859-15">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function () {
+  assert_equals(document.characterSet, "ISO-8859-15");
+}, "Encoding specified in the 'charset' attribute should have precedence over 'content' attribute.");
+</script>
+</body>
+</html>
diff --git a/interfaces/SVG.idl b/interfaces/SVG.idl
index 2ab3741..01d87b8 100644
--- a/interfaces/SVG.idl
+++ b/interfaces/SVG.idl
@@ -673,11 +673,16 @@
 [Exposed=Window]
 interface SVGAElement : SVGGraphicsElement {
   [SameObject] readonly attribute SVGAnimatedString target;
-  [SameObject] readonly attribute SVGAnimatedString download;
-  [SameObject] readonly attribute SVGAnimatedString rel;
-  [SameObject] readonly attribute SVGAnimatedString relList;
-  [SameObject] readonly attribute SVGAnimatedString hreflang;
-  [SameObject] readonly attribute SVGAnimatedString type;
+  attribute DOMString download;
+  attribute USVString ping;
+  attribute DOMString rel;
+  [SameObject, PutsForward=value] readonly attribute DOMTokenList relList;
+  attribute DOMString hreflang;
+  attribute DOMString type;
+
+  attribute DOMString text;
+
+  attribute DOMString referrerPolicy;
 };
 
 SVGAElement includes SVGURIReference;
diff --git a/interfaces/animation-worklet.idl b/interfaces/animation-worklet.idl
index e0f5fc2..58d0bc5 100644
--- a/interfaces/animation-worklet.idl
+++ b/interfaces/animation-worklet.idl
@@ -1,35 +1,31 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "CSS Animation Worklet API" spec.
-// See: https://wicg.github.io/animation-worklet/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: CSS Animation Worklet API (https://wicg.github.io/animation-worklet/)
 
-partial interface Window {
+partial namespace CSS {
     [SameObject] readonly attribute Worklet animationWorklet;
 };
 
-callback VoidFunction = void ();
-
 [ Exposed=AnimationWorklet, Global=AnimationWorklet ]
 interface AnimationWorkletGlobalScope : WorkletGlobalScope {
     void registerAnimator(DOMString name, VoidFunction animatorCtor);
 };
 
 [Constructor (DOMString animatorName,
-              optional (AnimationEffectReadOnly or sequence)? effects = null,
+              optional (AnimationEffect or sequence<AnimationEffect>)? effects = null,
               optional AnimationTimeline? timeline,
               optional any options)]
 interface WorkletAnimation : Animation {
         readonly attribute DOMString animatorName;
 };
 
-interface WorkletGroupEffectReadOnly :  GroupEffectReadOnly {};
-
-interface WorkletGroupEffect :  WorkletGroupEffectReadOnly {};
-WorkletGroupEffect implements AnimationEffectMutable;
-WorkletGroupEffect implements GroupEffectMutable;
+interface WorkletGroupEffect {
+  sequence<AnimationEffect> getChildren();
+};
 
 [Exposed=AnimationWorklet]
-partial interface AnimationEffectReadOnly {
+partial interface AnimationEffect {
     // Intended for use inside Animation Worklet scope to drive the effect.
     attribute double localTime;
 };
diff --git a/interfaces/background-fetch.idl b/interfaces/background-fetch.idl
index fc3ed8d..6957604 100644
--- a/interfaces/background-fetch.idl
+++ b/interfaces/background-fetch.idl
@@ -1,10 +1,10 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "Background Fetch" spec.
-// See: https://wicg.github.io/background-fetch/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Background Fetch (https://wicg.github.io/background-fetch/)
 
 partial interface ServiceWorkerGlobalScope {
-  attribute EventHandler onbackgroundfetched;
+  attribute EventHandler onbackgroundfetchsuccess;
   attribute EventHandler onbackgroundfetchfail;
   attribute EventHandler onbackgroundfetchabort;
   attribute EventHandler onbackgroundfetchclick;
@@ -22,9 +22,12 @@
   // TODO: in future this should become an async iterator for BackgroundFetchRegistration objects
 };
 
-dictionary BackgroundFetchOptions {
+dictionary BackgroundFetchUIOptions {
   sequence<IconDefinition> icons = [];
   DOMString title = "";
+};
+
+dictionary BackgroundFetchOptions : BackgroundFetchUIOptions {
   unsigned long long downloadTotal = 0;
 };
 
@@ -43,73 +46,50 @@
   readonly attribute unsigned long long uploaded;
   readonly attribute unsigned long long downloadTotal;
   readonly attribute unsigned long long downloaded;
-  readonly attribute BackgroundFetchActiveFetches activeFetches;
+  readonly attribute BackgroundFetchState state;
+  readonly attribute BackgroundFetchFailureReason failureReason;
 
   attribute EventHandler onprogress;
 
   Promise<boolean> abort();
+  Promise<BackgroundFetchRecord> match(RequestInfo request, optional CacheQueryOptions options);
+  Promise<sequence<BackgroundFetchRecord>> matchAll(optional RequestInfo request, optional CacheQueryOptions options);
+};
+
+enum BackgroundFetchState { "pending", "success", "failure" };
+
+enum BackgroundFetchFailureReason {
+  "",
+  // The operation was aborted by the user, or abort() was called.
+  "aborted",
+  // A response had a not-ok-status.
+  "bad-status",
+  // A fetch failed for other reasons, e.g. CORS, MIX, an invalid partial response,
+  // or a general network failure for a fetch that cannot be retried.
+  "fetch-error",
+  // Storage quota was reached during the operation.
+  "quota-exceeded",
+  // The provided downloadTotal was exceeded.
+  "total-download-exceeded"
 };
 
 [Exposed=(Window,Worker)]
-interface BackgroundFetchActiveFetches {
-  Promise<BackgroundFetchActiveFetch> match(RequestInfo request, optional CacheQueryOptions options);
-  Promise<sequence<BackgroundFetchActiveFetch>> matchAll(RequestInfo request, optional CacheQueryOptions options);
-  Promise<sequence<BackgroundFetchActiveFetch>> values();
-};
-
-[Exposed=(Window,Worker)]
-interface BackgroundFetchActiveFetch : BackgroundFetchFetch {
-  readonly attribute Promise<Response> responseReady;
-  // In future this will include a fetch observer
-};
-
-[Exposed=(Window,Worker)]
-interface BackgroundFetchFetch {
+interface BackgroundFetchRecord {
   readonly attribute Request request;
+  readonly attribute Promise<Response> responseReady;
+  // TODO: In future this will include a fetch observer
 };
 
 [Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
 interface BackgroundFetchEvent : ExtendableEvent {
-  readonly attribute DOMString id;
+  readonly attribute BackgroundFetchRegistration registration;
 };
 
 dictionary BackgroundFetchEventInit : ExtendableEventInit {
-  required DOMString id;
+  required BackgroundFetchRegistration registration;
 };
 
-[Constructor(DOMString type, BackgroundFetchSettledEventInit init), Exposed=ServiceWorker]
-interface BackgroundFetchSettledEvent : BackgroundFetchEvent {
-  readonly attribute BackgroundFetchSettledFetches fetches;
+[Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
+interface BackgroundFetchUpdateUIEvent : BackgroundFetchEvent {
+  Promise<void> updateUI(optional BackgroundFetchUIOptions options);
 };
-
-dictionary BackgroundFetchSettledEventInit : BackgroundFetchEventInit {
-  required BackgroundFetchSettledFetches fetches;
-};
-
-[Exposed=ServiceWorker]
-interface BackgroundFetchSettledFetches {
-  Promise<BackgroundFetchSettledFetch> match(RequestInfo request, optional CacheQueryOptions options);
-  Promise<sequence<BackgroundFetchSettledFetch>> matchAll(RequestInfo request, optional CacheQueryOptions options);
-  Promise<sequence<BackgroundFetchSettledFetch>> values();
-};
-
-[Exposed=ServiceWorker]
-interface BackgroundFetchSettledFetch : BackgroundFetchFetch {
-  readonly attribute Response? response;
-};
-
-[Constructor(DOMString type, BackgroundFetchSettledEventInit init), Exposed=ServiceWorker]
-interface BackgroundFetchUpdateEvent : BackgroundFetchSettledEvent {
-  Promise<void> updateUI(DOMString title);
-};
-
-[Constructor(DOMString type, BackgroundFetchClickEventInit init), Exposed=ServiceWorker]
-interface BackgroundFetchClickEvent : BackgroundFetchEvent {
-  readonly attribute BackgroundFetchState state;
-};
-
-dictionary BackgroundFetchClickEventInit : BackgroundFetchEventInit {
-  required BackgroundFetchState state;
-};
-
-enum BackgroundFetchState { "pending", "succeeded", "failed" };
diff --git a/interfaces/css-paint-api.idl b/interfaces/css-paint-api.idl
index 442bbfa..c636e2e 100644
--- a/interfaces/css-paint-api.idl
+++ b/interfaces/css-paint-api.idl
@@ -1,9 +1,9 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "CSS Painting API Level 1" spec.
-// See: https://drafts.css-houdini.org/css-paint-api-1/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: CSS Painting API Level 1 (https://drafts.css-houdini.org/css-paint-api-1/)
 
-partial interface CSS {
+partial namespace CSS {
     [SameObject] readonly attribute Worklet paintWorklet;
 };
 
diff --git a/interfaces/payment-request.idl b/interfaces/payment-request.idl
index 1018ecc..3717d79 100644
--- a/interfaces/payment-request.idl
+++ b/interfaces/payment-request.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "Payment Request API" spec.
-// See: https://w3c.github.io/payment-request/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Payment Request API (https://w3c.github.io/payment-request/)
 
 [Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details, optional PaymentOptions options),
 SecureContext, Exposed=Window]
@@ -167,6 +167,8 @@
 dictionary PaymentValidationErrors {
   PayerErrorFields payer;
   AddressErrors shippingAddress;
+  DOMString error;
+  object paymentMethod;
 };
 
 dictionary PayerErrorFields {
diff --git a/interfaces/service-workers.idl b/interfaces/service-workers.idl
index 38d22c6..2c9cbf6 100644
--- a/interfaces/service-workers.idl
+++ b/interfaces/service-workers.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "Service Workers 1" spec.
-// See: https://w3c.github.io/ServiceWorker/v1/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Service Workers 1 (https://w3c.github.io/ServiceWorker/v1/)
 
 [SecureContext, Exposed=(Window,Worker)]
 interface ServiceWorker : EventTarget {
diff --git a/interfaces/speech-api.idl b/interfaces/speech-api.idl
index 5ffa04f..8e998df 100644
--- a/interfaces/speech-api.idl
+++ b/interfaces/speech-api.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "Web Speech API" spec.
-// See: https://w3c.github.io/speech-api/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: Web Speech API (https://w3c.github.io/speech-api/)
 
 [Exposed=Window, Constructor]
 interface SpeechRecognition : EventTarget {
@@ -157,6 +157,7 @@
     "voice-unavailable",
     "text-too-long",
     "invalid-argument",
+    "not-allowed",
 };
 
 [Exposed=Window]
diff --git a/interfaces/webrtc.idl b/interfaces/webrtc.idl
index 2849a7d..c06e91e 100644
--- a/interfaces/webrtc.idl
+++ b/interfaces/webrtc.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "WebRTC 1.0: Real-time Communication Between Browsers" spec.
-// See: https://w3c.github.io/webrtc-pc/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: WebRTC 1.0: Real-time Communication Between Browsers (https://w3c.github.io/webrtc-pc/)
 
 dictionary RTCConfiguration {
              sequence<RTCIceServer> iceServers;
@@ -105,7 +105,7 @@
     readonly        attribute RTCSessionDescription? remoteDescription;
     readonly        attribute RTCSessionDescription? currentRemoteDescription;
     readonly        attribute RTCSessionDescription? pendingRemoteDescription;
-    Promise<void> addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate);
+    Promise<void> addIceCandidate(RTCIceCandidateInit candidate);
     readonly        attribute RTCSignalingState signalingState;
     readonly        attribute RTCIceGatheringState iceGatheringState;
     readonly        attribute RTCIceConnectionState iceConnectionState;
@@ -129,7 +129,7 @@
     Promise<void> setLocalDescription(RTCSessionDescriptionInit description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
     Promise<void> createAnswer(RTCSessionDescriptionCallback successCallback, RTCPeerConnectionErrorCallback failureCallback);
     Promise<void> setRemoteDescription(RTCSessionDescriptionInit description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
-    Promise<void> addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
+    Promise<void> addIceCandidate(RTCIceCandidateInit candidate, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);
 };
 
 callback RTCPeerConnectionErrorCallback = void (DOMException error);
diff --git a/interfaces/webxr.idl b/interfaces/webxr.idl
index 563c4e0..00839ee 100644
--- a/interfaces/webxr.idl
+++ b/interfaces/webxr.idl
@@ -97,12 +97,7 @@
 };
 
 [SecureContext, Exposed=Window] interface XRStageBounds {
-  readonly attribute FrozenArray<XRStageBoundsPoint> geometry;
-};
-
-[SecureContext, Exposed=Window] interface XRStageBoundsPoint {
-  readonly attribute double x;
-  readonly attribute double z;
+  readonly attribute FrozenArray<DOMPointReadOnly> geometry;
 };
 
 enum XREye {
diff --git a/intersection-observer/observer-exceptions.html b/intersection-observer/observer-exceptions.html
index d4f178b..5d29234 100644
--- a/intersection-observer/observer-exceptions.html
+++ b/intersection-observer/observer-exceptions.html
@@ -19,7 +19,7 @@
   assert_throws("SYNTAX_ERR", function() {
     new IntersectionObserver(e => {}, {rootMargin: "1"})
   })
-}, 'IntersectionObserver constructor witth { rootMargin: "1" }');
+}, 'IntersectionObserver constructor with { rootMargin: "1" }');
 
 test(function () {
   assert_throws("SYNTAX_ERR", function() {
diff --git a/keyboard-lock/idlharness.https.window.js b/keyboard-lock/idlharness.https.window.js
index 27573bd..7a20d8b 100644
--- a/keyboard-lock/idlharness.https.window.js
+++ b/keyboard-lock/idlharness.https.window.js
@@ -13,6 +13,5 @@
       Navigator: ['navigator'],
       Keyboard: ['navigator.keyboard'],
     });
-  },
-  'keyboard-lock interfaces'
+  }
 );
diff --git a/keyboard-map/idlharness.https.window.js b/keyboard-map/idlharness.https.window.js
index d800166..1842136 100644
--- a/keyboard-map/idlharness.https.window.js
+++ b/keyboard-map/idlharness.https.window.js
@@ -16,6 +16,5 @@
     });
 
     self.layout_map = await navigator.keyboard.getLayoutMap();
-  },
-  'Test IDL implementation of Keyboard Map API'
+  }
 );
diff --git a/magnetometer/idlharness.https.window.js b/magnetometer/idlharness.https.window.js
index 97a3c85..e6b8f7d 100644
--- a/magnetometer/idlharness.https.window.js
+++ b/magnetometer/idlharness.https.window.js
@@ -13,6 +13,5 @@
       Magnetometer: ['new Magnetometer();'],
       UncalibratedMagnetometer: ['new UncalibratedMagnetometer();']
     });
-  },
-  'Test IDL implementation of Magnetometer Sensor'
+  }
 );
diff --git a/media-capabilities/idlharness.any.js b/media-capabilities/idlharness.any.js
index 0be4674..bd20d5b 100644
--- a/media-capabilities/idlharness.any.js
+++ b/media-capabilities/idlharness.any.js
@@ -41,7 +41,6 @@
         Screen: ['screen'],
         ScreenLuminance: ['screen.luminance'],
       });
-    },
-    'Test IDL implementation of Media Capabilities'
+    }
   );
 });
diff --git a/media/video.ogv b/media/video.ogv
new file mode 100644
index 0000000..5cb5f87
--- /dev/null
+++ b/media/video.ogv
Binary files differ
diff --git a/mediacapture-fromelement/idlharness.window.js b/mediacapture-fromelement/idlharness.window.js
index 25c8233..c58e63f 100644
--- a/mediacapture-fromelement/idlharness.window.js
+++ b/mediacapture-fromelement/idlharness.window.js
@@ -28,6 +28,5 @@
       HTMLCanvasElement: ['canvas'],
       CanvasCaptureMediaStreamTrack: ['track'],
     });
-  },
-  'Test mediacapture-fromelement IDL interfaces'
+  }
 );
diff --git a/mediacapture-image/idlharness.window.js b/mediacapture-image/idlharness.window.js
index 0c06405..9d8f7ee 100644
--- a/mediacapture-image/idlharness.window.js
+++ b/mediacapture-image/idlharness.window.js
@@ -22,6 +22,5 @@
     const track = canvas.captureStream().getVideoTracks()[0];
     self.capture = new ImageCapture(track);
     self.capabilities = await capture.getPhotoCapabilities();
-  },
-  'Test mediacapture-image IDL interfaces'
+  }
 );
diff --git a/mediacapture-record/idlharness.window.js b/mediacapture-record/idlharness.window.js
index 3b79474..86f5a15 100644
--- a/mediacapture-record/idlharness.window.js
+++ b/mediacapture-record/idlharness.window.js
@@ -34,6 +34,5 @@
       error = new MediaRecorderErrorEvent("type", {});
     } catch(e) {}
     idl_array.add_objects({ MediaRecorderErrorEvent: [error] });
-  },
-  'mediastream-recording interfaces'
+  }
 );
diff --git a/mediasession/idlharness.window.js b/mediasession/idlharness.window.js
index f9ac62d..5fa0d22 100644
--- a/mediasession/idlharness.window.js
+++ b/mediasession/idlharness.window.js
@@ -14,6 +14,5 @@
       MediaSession: ['navigator.mediaSession'],
       Navigator: ['navigator']
     });
-  },
-  'Test IDL implementation of Media Session'
+  }
 );
diff --git a/navigation-timing/idlharness.window.js b/navigation-timing/idlharness.window.js
index a782773..8cc3546 100644
--- a/navigation-timing/idlharness.window.js
+++ b/navigation-timing/idlharness.window.js
@@ -17,6 +17,5 @@
         'performance.getEntriesByType("navigation")[0]'
       ]
     });
-  },
-  'navigation-timing interfaces'
+  }
 );
diff --git a/navigation-timing/nav2_idlharness.html b/navigation-timing/nav2_idlharness.html
deleted file mode 100644
index 6d1a124..0000000
--- a/navigation-timing/nav2_idlharness.html
+++ /dev/null
@@ -1,73 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>Navigation Timing 2 IDL tests</title>
-<link rel="author" title="Google" href="http://www.google.com/" />
-<link rel="help" href="http://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming"/>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/WebIDLParser.js"></script>
-<script src="/resources/idlharness.js"></script>
-</head>
-<body>
-
-<pre id='untested_idl' style='display:none'>
-
-interface PerformanceResourceTiming : PerformanceEntry {
-    readonly attribute DOMString initiatorType;
-    readonly attribute DOMHighResTimeStamp workerStart;
-    readonly attribute DOMHighResTimeStamp redirectStart;
-    readonly attribute DOMHighResTimeStamp redirectEnd;
-    readonly attribute DOMHighResTimeStamp fetchStart;
-    readonly attribute DOMHighResTimeStamp domainLookupStart;
-    readonly attribute DOMHighResTimeStamp domainLookupEnd;
-    readonly attribute DOMHighResTimeStamp connectStart;
-    readonly attribute DOMHighResTimeStamp connectEnd;
-    readonly attribute DOMHighResTimeStamp secureConnectionStart;
-    readonly attribute DOMHighResTimeStamp requestStart;
-    readonly attribute DOMHighResTimeStamp responseStart;
-    readonly attribute DOMHighResTimeStamp responseEnd;
-    [MeasureAs=PerformanceResourceTimingSizes] readonly attribute unsigned long long transferSize;
-    [MeasureAs=PerformanceResourceTimingSizes] readonly attribute unsigned long long encodedBodySize;
-    [MeasureAs=PerformanceResourceTimingSizes] readonly attribute unsigned long long decodedBodySize;
-};
-interface PerformanceEntry {};
-</pre>
-
-<pre id='idl'>
-enum NavigationType {
-    "navigate",
-    "reload",
-    "back_forward",
-    "prerender"
-};
-
-interface PerformanceNavigationTiming : PerformanceResourceTiming {
-    readonly attribute DOMHighResTimeStamp unloadEventStart;
-    readonly attribute DOMHighResTimeStamp unloadEventEnd;
-    readonly attribute DOMHighResTimeStamp domInteractive;
-    readonly attribute DOMHighResTimeStamp domContentLoadedEventStart;
-    readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd;
-    readonly attribute DOMHighResTimeStamp domComplete;
-    readonly attribute DOMHighResTimeStamp loadEventStart;
-    readonly attribute DOMHighResTimeStamp loadEventEnd;
-    readonly attribute NavigationType      type;
-    readonly attribute unsigned short      redirectCount;
-    [Default] object toJSON();
-};
-</pre>
-
-<script>
-function test_idl() {
-  var idl_array = new IdlArray();
-
-  idl_array.add_untested_idls(document.getElementById("untested_idl").textContent);
-  idl_array.add_idls(document.getElementById("idl").textContent);
-
-  idl_array.test();
-}
-test_idl();
-</script>
-</body>
-</html>
diff --git a/netinfo/idlharness.any.js b/netinfo/idlharness.any.js
index d8e1561..4610508 100644
--- a/netinfo/idlharness.any.js
+++ b/netinfo/idlharness.any.js
@@ -15,5 +15,5 @@
     } else {
       idl_array.add_objects({ Navigator: ['navigator'] });
     }
-  },
-  'netinfo interfaces.');
+  }
+);
diff --git a/orientation-sensor/idlharness.https.window.js b/orientation-sensor/idlharness.https.window.js
index 39217fc..86f2774 100644
--- a/orientation-sensor/idlharness.https.window.js
+++ b/orientation-sensor/idlharness.https.window.js
@@ -14,6 +14,5 @@
       RelativeOrientationSensor: ['new RelativeOrientationSensor();']
     });
     idl_array.prevent_multiple_testing('OrientationSensor');
-  },
-  'Test IDL implementation of Orientation Sensor'
+  }
 );
diff --git a/page-visibility/idlharness.window.js b/page-visibility/idlharness.window.js
index 8ef2561..8cc2f83 100644
--- a/page-visibility/idlharness.window.js
+++ b/page-visibility/idlharness.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       Document: ['document'],
     });
-  },
-  'page-visibility interfaces'
+  }
 );
diff --git a/paint-timing/idlharness.window.js b/paint-timing/idlharness.window.js
index f108cc4..115af73 100644
--- a/paint-timing/idlharness.window.js
+++ b/paint-timing/idlharness.window.js
@@ -24,5 +24,5 @@
       t.step_timeout(() => reject('Timed out waiting for paint event'), 3000);
     });
     return Promise.race([awaitPaint, timeout]);
-  },
-  'paint-timing interfaces.');
+  }
+);
diff --git a/payment-method-basic-card/idlharness.window.js b/payment-method-basic-card/idlharness.window.js
index a1078c6..cd158a6 100644
--- a/payment-method-basic-card/idlharness.window.js
+++ b/payment-method-basic-card/idlharness.window.js
@@ -9,4 +9,4 @@
   ['payment-method-basic-card'],
   [], // No deps
   null, // No objects
-  'payment-method-basic-card interfaces.');
+);
diff --git a/payment-request/PaymentValidationErrors/retry-shows-error-member-manual.https.html b/payment-request/PaymentValidationErrors/retry-shows-error-member-manual.https.html
new file mode 100644
index 0000000..b3a539e
--- /dev/null
+++ b/payment-request/PaymentValidationErrors/retry-shows-error-member-manual.https.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentvalidationerrors-error">
+<title>
+  PaymentValidationErrors' `error` member
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../payment-response/helpers.js"></script>
+<script>
+function retryShowsErrorMember(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    await response.retry({ error: "PASS" });
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+</script>
+<h2>
+  Manual Test for PaymentValidationErrors's `error` member - Please run in order!
+</h2>
+<p>
+  Click on each button in sequence from top to bottom without refreshing the page.
+  Each button will bring up the Payment Request UI window.
+</p>
+<p>
+  When presented with the payment sheet, use any card and select to "Pay".
+  You will be asked to retry the payment and an error should be shown somewhere
+  in the UI. The expected error string is described in each individual test.
+  If you see the error, hit "Pay" again. If you don't see the error,
+  abort the payment request by hitting "esc" - which means that particular test
+  has failed.
+</p>
+<ol>
+  <li>
+    <button onclick="retryShowsErrorMember(this);">
+      The payment sheet shows the error "PASS" somewhere in the UI.
+    </button>
+  </li>
+  <li>
+    <button onclick="done();">
+      Done!
+    </button>
+  </li>
+</ol>
+<small>
+  If you find a buggy test, please <a href="https://github.com/w3c/web-platform-tests/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/w3c/web-platform-tests/blob/master/payment-request/OWNERS">owners</a>.
+</small>
diff --git a/payment-request/PaymentValidationErrors/retry-shows-payer-member-manual.https.html b/payment-request/PaymentValidationErrors/retry-shows-payer-member-manual.https.html
new file mode 100644
index 0000000..8dbc760
--- /dev/null
+++ b/payment-request/PaymentValidationErrors/retry-shows-payer-member-manual.https.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentvalidationerrors-payer">
+<title>
+  PaymentValidationErrors' `payer` member
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../payment-response/helpers.js"></script>
+<script>
+function retryShowsPayerMember(button, error) {
+  button.disabled = true;
+  promise_test(async t => {
+    const options = {
+      requestPayerName: true,
+      requestPayerEmail: true,
+      requestPayerPhone: true,
+    }
+    const { response } = await getPaymentRequestResponse(options);
+    await response.retry({ payer: error });
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+</script>
+<h2>
+  Manual Test for PaymentValidationErrors' `payer` member - Please run in order!
+</h2>
+<p>
+  Click on each button in sequence from top to bottom without refreshing the page.
+  Each button will bring up the Payment Request UI window.
+</p>
+<p>
+  When presented with the payment sheet, use any card and select to "Pay".
+  You will be asked to retry the payment and an error should be shown somewhere
+  in the UI. The expected error string is described in each individual test.
+  If you see the error, hit "Pay" again. If you don't see the error,
+  abort the payment request by hitting "esc" - which means that particular test
+  has failed.
+</p>
+<ol>
+  <li>
+    <button onclick="retryShowsPayerMember(this, { email: 'EMAIL ERROR' });">
+      The payment sheet shows "EMAIL ERROR" for the payer's email.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsPayerMember(this, { name: 'NAME ERROR' });">
+      The payment sheet shows "NAME ERROR" for the payer's name.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsPayerMember(this, { phone: 'PHONE ERROR' });">
+      The payment sheet shows "PHONE ERROR" for the payer's phone number.
+    </button>
+  </li>
+  <li>
+    <button onclick="done();">
+      Done!
+    </button>
+  </li>
+</ol>
+<small>
+  If you find a buggy test, please <a href="https://github.com/w3c/web-platform-tests/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/w3c/web-platform-tests/blob/master/payment-request/OWNERS">owners</a>.
+</small>
diff --git a/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html b/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
new file mode 100644
index 0000000..cdc8d35
--- /dev/null
+++ b/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentvalidationerrors-shippingaddress">
+<title>
+  PaymentValidationErrors' `shippingAddress` member (AddressErrors)
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../payment-response/helpers.js"></script>
+<script>
+function retryShowsShippingAddressMember(button, error) {
+  button.disabled = true;
+  promise_test(async t => {
+    const options = {
+      requestShipping: true,
+    }
+    const { response } = await getPaymentRequestResponse(options);
+    await response.retry({ shippingAddress: error });
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+</script>
+<h2>
+  Manual Test for PaymentValidationErrors' `shippingAddress` member - Please run in order!
+</h2>
+<p>
+  Click on each button in sequence from top to bottom without refreshing the page.
+  Each button will bring up the Payment Request UI window.
+</p>
+<p>
+  When presented with the payment sheet, use any card and select to "Pay".
+  You will be asked to retry the payment and an error should be shown somewhere
+  in the UI. The expected error string is described in each individual test.
+  If you see the error, hit "Pay" again. If you don't see the error,
+  abort the payment request by hitting "esc" - which means that particular test
+  has failed.
+</p>
+<ol>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { addressLine: 'ADDRESSLINE ERROR' });">
+      The payment sheet shows "ADDRESSLINE ERROR" for the shipping address' addressLine.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { city: 'CITY ERROR' });">
+      The payment sheet shows "CITY ERROR" for the shipping address' city.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { country: 'COUNTRY ERROR' });">
+      The payment sheet shows "COUNTRY ERROR" for the shipping address' country.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { dependentLocality: 'DEPENDENTLOCALITY ERROR' });">
+      The payment sheet shows "DEPENDENTLOCALITY ERROR" for the shipping address' dependentLocality.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { languageCode: 'LANGUAGECODE ERROR' });">
+      The payment sheet shows "LANGUAGECODE ERROR" for the shipping address' languageCode.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { organization: 'ORGANIZATION ERROR' });">
+      The payment sheet shows "ORGANIZATION ERROR" for the shipping address' organization.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { phone: 'PHONE ERROR' });">
+      The payment sheet shows "PHONE ERROR" for the shipping address' phone.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { postalCode: 'POSTALCODE ERROR' });">
+      The payment sheet shows "POSTALCODE ERROR" for the shipping address' postal code.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { recipient: 'RECIPIENT ERROR' });">
+      The payment sheet shows "RECIPIENT ERROR" for the shipping address' recipient.
+    </button>
+  </li>
+  <li>
+    <button onclick="retryShowsShippingAddressMember(this, { region: 'REGION ERROR' });">
+      The payment sheet shows "REGION ERROR" for the shipping address' region.
+    </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>
+  </li>
+  <li>
+    <button onclick="done();">
+      Done!
+    </button>
+  </li>
+</ol>
+<small>
+  If you find a buggy test, please <a href="https://github.com/w3c/web-platform-tests/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/w3c/web-platform-tests/blob/master/payment-request/OWNERS">owners</a>.
+</small>
diff --git a/payment-request/idlharness.https.window.js b/payment-request/idlharness.https.window.js
index 57a2c94..9a068c6 100644
--- a/payment-request/idlharness.https.window.js
+++ b/payment-request/idlharness.https.window.js
@@ -26,6 +26,5 @@
       PaymentMethodChangeEvent: ['new PaymentMethodChangeEvent("paymentmethodchange")'],
       PaymentRequestUpdateEvent: ['new PaymentRequestUpdateEvent("paymentrequestupdate")'],
     });
-  },
-  'Setup for Payment Request API IDL tests.'
+  }
 );
diff --git a/payment-request/payment-response/rejects_if_not_active-manual.https.html b/payment-request/payment-response/rejects_if_not_active-manual.https.html
new file mode 100644
index 0000000..60dd965
--- /dev/null
+++ b/payment-request/payment-response/rejects_if_not_active-manual.https.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="help" href="https://w3c.github.io/payment-request/#retry-method">
+<title>PaymentResponse retry() rejects if doc is not fully active</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry">
+<body>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const validMethod = Object.freeze({
+  supportedMethods: "basic-card",
+});
+const validMethods = Object.freeze([validMethod]);
+const validAmount = Object.freeze({
+  currency: "USD",
+  value: "5.00",
+});
+const validTotal = Object.freeze({
+  label: "Total due",
+  amount: validAmount,
+});
+const validDetails = Object.freeze({
+  total: validTotal,
+});
+
+function getLoadedPaymentResponse(iframe, url) {
+  return new Promise(resolve => {
+    iframe.addEventListener(
+      "load",
+      async () => {
+        const { PaymentRequest } = iframe.contentWindow;
+        const response = await new PaymentRequest(
+          validMethods,
+          validDetails
+        ).show();
+        resolve(response);
+      },
+      { once: true }
+    );
+    iframe.src = url;
+  });
+}
+
+function methodNotFullyActive(button, method, ...args) {
+  const text = button.textContent.trim();
+  promise_test(async t => {
+    const iframe = document.createElement("iframe");
+    iframe.allowPaymentRequest = true;
+    document.body.appendChild(iframe);
+
+    // We first got to page1.html, grab a PaymentResponse instance.
+    const response = await getLoadedPaymentResponse(
+      iframe,
+      "/payment-request/resources/page1.html"
+    );
+    // We navigate the iframe again, putting response's document into an inactive state.
+    await new Promise(resolve => {
+      iframe.addEventListener("load", resolve);
+      iframe.src = "/payment-request/resources/page2.html";
+    });
+    // Now, response's relevant global object's document is no longer active.
+    // So, promise needs to reject appropriately.
+    const promise = response[methodName](...args);
+    await promise_rejects(
+      t,
+      "AbortError",
+      promise,
+      "Inactive document, so must throw AbortError"
+    );
+    // We are done, so clean up.
+    iframe.remove();
+  }, text);
+}
+
+function methodBecomesNotFullyActive(button, methodName, ...args) {
+  const text = button.textContent.trim();
+  promise_test(async t => {
+    const iframe = document.createElement("iframe");
+    iframe.allowPaymentRequest = true;
+    document.body.appendChild(iframe);
+
+    // We first got to page1.html, grab a PaymentResponse instance.
+    const response = await getLoadedPaymentResponse(
+      iframe,
+      "/payment-request/resources/page1.html"
+    );
+
+    // we get the promise from page1.html, while it's active!
+    const promise = response[methodName](...args);
+
+    // We navigate the iframe again, putting response's document into an inactive state.
+    await new Promise(resolve => {
+      iframe.addEventListener("load", resolve);
+      iframe.src = "/payment-request/resources/page2.html";
+    });
+
+    // Now, response's relevant global object's document is no longer active.
+    // So, promise needs to reject appropriately.
+    await promise_rejects(
+      t,
+      "AbortError",
+      promise,
+      "Inactive document, so must throw AbortError"
+    );
+    // We are done, so clean up.
+    iframe.remove();
+  }, text);
+}
+</script>
+<section>
+  <p>
+    For each test, when the payment sheet is shown, select a payment method and hit "Pay".
+  </p>
+  <h2>retry() and document active state</h2>
+  <p>Manual Tests for PaymentResponse.retry() - Please run in order!</p>
+  <ol>
+    <li>
+      <button onclick="methodNotFullyActive(this, 'retry', {});">
+        retry()'s retryPromise rejects if document is not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="methodBecomesNotFullyActive(this, 'retry', {});">
+        retry()'s retryPromise rejects if the document becomes not fully active.
+      </button>
+    </li>
+  </ol>
+  <h2>complete() and document active state</h2>
+  <p>Manual Tests for PaymentResponse.complete() - Please run in order!</p>
+  <ol>
+    <li>
+      <button onclick="methodNotFullyActive(this, 'complete', 'success');">
+        complete()'s completePromise rejects if document is not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="methodBecomesNotFullyActive(this, 'complete', 'success');">
+        complete()'s completePromise rejects if the document becomes not fully active.
+      </button>
+    </li>
+    <li>
+      <button onclick="done();">Done</button>
+    </li>
+  </ol>
+</section>
+<small>
+  If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/payment-request/payment-response/retry-method-manual.https.html b/payment-request/payment-response/retry-method-manual.https.html
new file mode 100644
index 0000000..288a32b
--- /dev/null
+++ b/payment-request/payment-response/retry-method-manual.https.html
@@ -0,0 +1,262 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry">
+<title>
+  PaymentResponse.prototype.retry() method
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helpers.js"></script>
+<script>
+test(() => {
+  assert_true(
+    "retry" in PaymentResponse.prototype,
+    "retry must be in prototype"
+  );
+  assert_true(
+    PaymentResponse.prototype.retry instanceof Function,
+    "retry must be a function"
+  );
+}, "PaymentResponse.prototype must have a retry() function (smoke test).");
+
+function checkCompletedCantRetry(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    // sets response.[[complete]] to true.
+    await response.complete("success");
+    return promise_rejects(
+      t,
+      "InvalidStateError",
+      response.retry({}),
+      "response.[[complete]] is true, so rejects with InvalidStateError."
+    );
+  }, button.textContent.trim());
+}
+
+function repeatedCallsToRetry(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    const retryPromise = response.retry({});
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.retry({}),
+      "Calling retry() again rejects with an InvalidStateError"
+    );
+    await retryPromise;
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+
+function callCompleteWhileRetrying(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    const retryPromise = response.retry({});
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.complete("success"),
+      "Calling complete() while retrying rejects with an InvalidStateError"
+    );
+    await retryPromise;
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+
+function callingRequestAbortMustNotAbort(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response, request } = await getPaymentRequestResponse();
+    const retryPromise = response.retry({});
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      request.abort(),
+      "Calling request.abort() while retrying rejects with an InvalidStateError"
+    );
+    await retryPromise;
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+
+function canRetryMultipleTimes(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    assert_equals(
+      await response.retry({}),
+      undefined,
+      "Expected undefined as the resolve value"
+    );
+    assert_equals(
+      await response.retry({}),
+      undefined,
+      "Expected undefined as the resolve value"
+    );
+    await response.complete("success");
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.retry({}),
+      "Calling retry() after complete() rejects with a InvalidStateError"
+    );
+  }, button.textContent.trim());
+}
+
+function userCanAbortARetry(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response } = await getPaymentRequestResponse();
+    await promise_rejects(
+      t,
+      "AbortError",
+      response.retry({}),
+      "The user aborting a retry rejects with a AbortError"
+    );
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.retry({}),
+      "After the user aborts, response [[complete]] is true so retry() must reject with InvalidStateError"
+    );
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.complete("success"),
+      "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
+    );
+  }, button.textContent.trim());
+}
+
+function abortTheUpdate(button) {
+  button.disabled = true;
+  promise_test(async t => {
+    const { response, request } = await getPaymentRequestResponse();
+    // causes "abort the update" to run
+    const shippingChangedPromise = new Promise(resolve => {
+      request.onshippingoptionchange = () => {
+        event.updateWith({ total: { amount: { currency: "USD", value: "INVALID" } }});
+        resolve();
+      };
+    });
+    const retryPromise = response.retry({});
+    await shippingChangedPromise;
+    await promise_rejects(
+      t,
+      "TypeError",
+      retryPromise,
+      "retry() aborts with a TypeError, because totals' value is invalid"
+    );
+    await promise_rejects(
+      t,
+      "InvalidStateError",
+      response.complete("success"),
+      "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
+    );
+  }, button.textContent.trim());
+}
+
+function callingRetryReturnsUniquePromise(button){
+  button.disabled = true;
+  promise_test(async t => {
+    const request = new PaymentRequest(defaultMethods, defaultDetails);
+    const retryPromise = request.retry();
+    const promises = new Set([
+      retryPromise,
+      request.retry(),
+      request.retry(),
+    ]);
+    assert_equals(promises.size, 3, "Must have three unique objects");
+    await retryPromise;
+    await response.complete();
+  }, button.textContent.trim());
+};
+
+
+</script>
+<h2>
+    Manual Tests for PaymentResponse.retry() - Please run in order!
+</h2>
+<p>
+  Click on each button in sequence from top to bottom without refreshing the page.
+  Each button will bring up the Payment Request UI window.
+</p>
+<p>
+  When presented with the payment sheet, use any credit card select to "Pay" multiple times.
+</p>
+<ol>
+  <li>
+    <button onclick="checkCompletedCantRetry(this);">
+      A completed payment request cannot be retried.
+    </button>
+  </li>
+  <li>
+    <button onclick="repeatedCallsToRetry(this);">
+      Calling retry() more than once yield a rejected promise, but the
+      retryPromise resolves independently.
+    </button>
+  </li>
+  <li>
+    <button onclick="callCompleteWhileRetrying(this);">
+      Calling complete() while a retry() is in progress results in an InvalidStateError.
+    </button>
+  </li>
+  <li>
+    <button onclick="callingRequestAbortMustNotAbort(this);">
+      Trying to abort() via PaymentRequest is not possible.
+    </button>
+  </li>
+  <li>
+    <button onclick="canRetryMultipleTimes(this);">
+      It's possible to retry() multiple times and eventually complete().
+      After complete(), however, retry() rejects with an InvalidStateError.
+    </button>
+  </li>
+  <li>
+    <p>
+      When shown the payment sheet, hit pay once, then abort retrying the payment.
+    </p>
+    <button onclick="userCanAbortARetry(this);">
+      The user aborting retrying a payment causes the retryPromise to reject with AbortError.
+      Aborting a payment is causes it complete.
+    </button>
+  </li>
+  <li>
+    <p>
+      When shown the payment sheet, hit pay once. Check payment sheet for error fields.
+      Then hit escape or otherwise abort the payment.
+    </p>
+    <button onclick="userIsShownErrorsFields(this);">
+      When retrying, the user is shown error fields to fix.
+    </button>
+  </li>
+  <li>
+    <p>
+      When shown the payment sheet, hit pay once. Then retry once.
+    </p>
+    <button onclick="abortTheUpdate(this);">
+      When "abort the update" occurs because of an update error,
+      the `retryPromise` is rejected and response.[[complete]] becomes true.
+    </button>
+  </li>
+  <li>
+    <p>
+      When shown the payment sheet, hit pay once. Then retry once.
+    </p>
+    <button onclick="callingRetryReturnsUniquePromise(this);">
+      Calling retry() multiple times is always a new object.
+    </button>
+  </li>
+  <li>
+    <button onclick="done();">
+      Done!
+    </button>
+  </li>
+</ol>
+<small>
+  If you find a buggy test, please <a href="https://github.com/w3c/web-platform-tests/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/w3c/web-platform-tests/blob/master/payment-request/OWNERS">owners</a>.
+</small>
diff --git a/performance-timeline/idlharness.any.js b/performance-timeline/idlharness.any.js
index a1a6343..585bda9 100644
--- a/performance-timeline/idlharness.any.js
+++ b/performance-timeline/idlharness.any.js
@@ -37,8 +37,7 @@
           PerformanceObserver: ['observer'],
           PerformanceObserverEntryList: ['entryList'],
         });
-      },
-      'Test IDL implementation of performance-timeline API'
+      }
     );
   };
 
diff --git a/picture-in-picture/idlharness.window.js b/picture-in-picture/idlharness.window.js
index ac4c0bf..a7f25e3 100644
--- a/picture-in-picture/idlharness.window.js
+++ b/picture-in-picture/idlharness.window.js
@@ -21,6 +21,5 @@
 
     self.video = await loadVideo();
     self.pipw = await requestPictureInPictureWithTrustedClick(video);
-  },
-  'picture-in-picture interfaces.'
+  }
 );
diff --git a/pointerevents/extension/idlharness.window.js b/pointerevents/extension/idlharness.window.js
index 703e1c4..895a891 100644
--- a/pointerevents/extension/idlharness.window.js
+++ b/pointerevents/extension/idlharness.window.js
@@ -10,6 +10,5 @@
     idl_array.add_objects({
       PointerEvent: ['new PointerEvent("pointer")'],
     })
-  },
-  'pointerevents extension interfaces'
+  }
 );
diff --git a/pointerevents/idlharness.window.js b/pointerevents/idlharness.window.js
index aa9b11c..89a994a 100644
--- a/pointerevents/idlharness.window.js
+++ b/pointerevents/idlharness.window.js
@@ -15,6 +15,5 @@
       Navigator: ['navigator'],
       PointerEvent: ['new PointerEvent("type")']
     });
-  },
-  'pointerevents interfaces'
+  }
 );
diff --git a/pointerlock/interfaces.window.js b/pointerlock/interfaces.window.js
index 99e2b44..f176343 100644
--- a/pointerlock/interfaces.window.js
+++ b/pointerlock/interfaces.window.js
@@ -14,5 +14,5 @@
       Element: ["window.document.documentElement"],
       MouseEvent: ["new MouseEvent('foo')"]
     });
-  },
-  'pointerlock interfaces.');
+  }
+);
diff --git a/proximity/idlharness.https.window.js b/proximity/idlharness.https.window.js
index 0827065..bf27f27 100644
--- a/proximity/idlharness.https.window.js
+++ b/proximity/idlharness.https.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       ProximitySensor: ['new ProximitySensor();']
     });
-  },
-  'Test IDL implementation of Proximity Sensor'
+  }
 );
diff --git a/remote-playback/idlharness.window.js b/remote-playback/idlharness.window.js
index 7309f8c..73bc998 100644
--- a/remote-playback/idlharness.window.js
+++ b/remote-playback/idlharness.window.js
@@ -24,6 +24,5 @@
       HTMLVideoElement: ['media'],
       RemotePlayback: ['media.remote']
     });
-  },
-  'remote-playback interfaces'
+  }
 );
diff --git a/resize-observer/idlharness.window.js b/resize-observer/idlharness.window.js
index 240f5ec..592af2e 100644
--- a/resize-observer/idlharness.window.js
+++ b/resize-observer/idlharness.window.js
@@ -33,6 +33,5 @@
         }
       ]);
     await helper.start();
-  },
-  'Test IDL implementation of ResizeObserver'
+  }
 );
diff --git a/resource-timing/idlharness.any.js b/resource-timing/idlharness.any.js
index 6dff5e8..a7542f1 100644
--- a/resource-timing/idlharness.any.js
+++ b/resource-timing/idlharness.any.js
@@ -19,6 +19,5 @@
       Performance: ['performance'],
       PerformanceResourceTiming: ['resource']
     });
-  },
-  'Test server-timing IDL implementation'
+  }
 );
diff --git a/resources/idlharness.js b/resources/idlharness.js
index 91818ef..368c96d 100644
--- a/resources/idlharness.js
+++ b/resources/idlharness.js
@@ -3197,7 +3197,7 @@
  *      as adding objects. Do not call idl_array.test() in the setup; it is
  *      called by this function (idl_test).
  */
-function idl_test(srcs, deps, idl_setup_func, test_name) {
+function idl_test(srcs, deps, idl_setup_func) {
     return promise_test(function (t) {
         var idl_array = new IdlArray();
         srcs = (srcs instanceof Array) ? srcs : [srcs] || [];
@@ -3221,7 +3221,7 @@
                 }
             })
             .catch(function(e) { setup_error = e || 'IDL setup failed.'; })
-            .finally(function () {
+            .then(function () {
                 var error = setup_error;
                 try {
                     idl_array.test(); // Test what we can.
@@ -3234,7 +3234,7 @@
                     throw error;
                 }
             });
-    }, test_name || 'idl_test setup');
+    }, 'idl_test setup');
 }
 
 /**
diff --git a/screen-orientation/idlharness.window.js b/screen-orientation/idlharness.window.js
index d4b80de..115f6cc 100644
--- a/screen-orientation/idlharness.window.js
+++ b/screen-orientation/idlharness.window.js
@@ -13,6 +13,5 @@
       Screen: ['screen'],
       ScreenOrientation: ['screen.orientation']
     });
-  },
-  'Test IDL implementation of Screen Orientation API'
+  }
 );
diff --git a/selection/idlharness.window.js b/selection/idlharness.window.js
index 7322a77..b211797 100644
--- a/selection/idlharness.window.js
+++ b/selection/idlharness.window.js
@@ -14,6 +14,5 @@
       Document: ['document'],
       Selection: ['getSelection()'],
     });
-  },
-  'selection-api interfaces'
+  }
 );
diff --git a/service-workers/cache-storage/resources/vary.py b/service-workers/cache-storage/resources/vary.py
new file mode 100644
index 0000000..59e39bc
--- /dev/null
+++ b/service-workers/cache-storage/resources/vary.py
@@ -0,0 +1,25 @@
+def main(request, response):
+  if "clear-vary-value-override-cookie" in request.GET:
+    response.unset_cookie("vary-value-override")
+    return "vary cookie cleared"
+
+  set_cookie_vary = request.GET.first("set-vary-value-override-cookie",
+                                      default="")
+  if set_cookie_vary:
+    response.set_cookie("vary-value-override", set_cookie_vary)
+    return "vary cookie set"
+
+  # If there is a vary-value-override cookie set, then use its value
+  # for the VARY header no matter what the query string is set to.  This
+  # override is necessary to test the case when two URLs are identical
+  # (including query), but differ by VARY header.
+  cookie_vary = request.cookies.get("vary-value-override");
+  if cookie_vary:
+    response.headers.set("vary", cookie_vary)
+  else:
+    # If there is no cookie, then use the query string value, if present.
+    query_vary = request.GET.first("vary", default="")
+    if query_vary:
+      response.headers.set("vary", query_vary)
+
+  return "vary response"
diff --git a/service-workers/cache-storage/script-tests/cache-add.js b/service-workers/cache-storage/script-tests/cache-add.js
index c03faeb..a482c42 100644
--- a/service-workers/cache-storage/script-tests/cache-add.js
+++ b/service-workers/cache-storage/script-tests/cache-add.js
@@ -267,4 +267,84 @@
       'twice.');
   }, 'Cache.addAll called with the same Request object specified twice');
 
+cache_test(async function(cache, test) {
+    const url = '../resources/vary.py?vary=x-shape';
+    let requests = [
+      new Request(url, { headers: { 'x-shape': 'circle' }}),
+      new Request(url, { headers: { 'x-shape': 'square' }}),
+    ];
+    let result = await cache.addAll(requests);
+    assert_equals(result, undefined, 'Cache.addAll() should succeed');
+  }, 'Cache.addAll should succeed when entries differ by vary header');
+
+cache_test(async function(cache, test) {
+    const url = '../resources/vary.py?vary=x-shape';
+    let requests = [
+      new Request(url, { headers: { 'x-shape': 'circle' }}),
+      new Request(url, { headers: { 'x-shape': 'circle' }}),
+    ];
+    await promise_rejects(
+      test,
+      'InvalidStateError',
+      cache.addAll(requests),
+      'Cache.addAll() should reject when entries are duplicate by vary header');
+  }, 'Cache.addAll should reject when entries are duplicate by vary header');
+
+// VARY header matching is asymmetric.  Determining if two entries are duplicate
+// depends on which entry's response is used in the comparison.  The target
+// response's VARY header determines what request headers are examined.  This
+// test verifies that Cache.addAll() duplicate checking handles this asymmetric
+// behavior correctly.
+cache_test(async function(cache, test) {
+    const base_url = '../resources/vary.py';
+
+    // Define a request URL that sets a VARY header in the
+    // query string to be echoed back by the server.
+    const url = base_url + '?vary=x-size';
+
+    // Set a cookie to override the VARY header of the response
+    // when the request is made with credentials.  This will
+    // take precedence over the query string vary param.  This
+    // is a bit confusing, but it's necessary to construct a test
+    // where the URL is the same, but the VARY headers differ.
+    //
+    // Note, the test could also pass this information in additional
+    // request headers.  If the cookie approach becomes too unwieldy
+    // this test could be rewritten to use that technique.
+    await fetch(base_url + '?set-vary-value-override-cookie=x-shape');
+    test.add_cleanup(_ => fetch(base_url + '?clear-vary-value-override-cookie'));
+
+    let requests = [
+      // This request will result in a Response with a "Vary: x-shape"
+      // header.  This *will not* result in a duplicate match with the
+      // other entry.
+      new Request(url, { headers: { 'x-shape': 'circle',
+                                    'x-size': 'big' },
+                         credentials: 'same-origin' }),
+
+      // This request will result in a Response with a "Vary: x-size"
+      // header.  This *will* result in a duplicate match with the other
+      // entry.
+      new Request(url, { headers: { 'x-shape': 'square',
+                                    'x-size': 'big' },
+                         credentials: 'omit' }),
+    ];
+    await promise_rejects(
+      test,
+      'InvalidStateError',
+      cache.addAll(requests),
+      'Cache.addAll() should reject when one entry has a vary header ' +
+      'matching an earlier entry.');
+
+    // Test the reverse order now.
+    await promise_rejects(
+      test,
+      'InvalidStateError',
+      cache.addAll(requests.reverse()),
+      'Cache.addAll() should reject when one entry has a vary header ' +
+      'matching a later entry.');
+
+  }, 'Cache.addAll should reject when one entry has a vary header ' +
+     'matching another entry');
+
 done();
diff --git a/service-workers/service-worker/postmessage.https.html b/service-workers/service-worker/postmessage.https.html
index 6b2f6d7..3d73afe 100644
--- a/service-workers/service-worker/postmessage.https.html
+++ b/service-workers/service-worker/postmessage.https.html
@@ -148,4 +148,46 @@
         });
   }, 'postMessage a transferable ArrayBuffer between ServiceWorker and Client' +
      ' over MessagePort');
+
+  promise_test(t => {
+    var script = 'resources/postmessage-dictionary-transferables-worker.js';
+    var scope = 'resources/blank.html';
+    var sw = navigator.serviceWorker;
+
+    var message = 'Hello, world!';
+    var text_encoder = new TextEncoder;
+    var text_decoder = new TextDecoder;
+
+    return service_worker_unregister_and_register(t, script, scope)
+      .then(r => {
+          t.add_cleanup(() => r.unregister());
+
+          var ab = text_encoder.encode(message);
+          assert_equals(ab.byteLength, message.length);
+          r.installing.postMessage(ab, {transfer: [ab.buffer]});
+          assert_equals(text_decoder.decode(ab), '');
+          assert_equals(ab.byteLength, 0);
+
+          return new Promise(resolve => { sw.onmessage = resolve; });
+        })
+      .then(e => {
+          // Verify the integrity of the transferred array buffer.
+          assert_equals(e.data.content, message);
+          assert_equals(e.data.byteLength, message.length);
+          return new Promise(resolve => { sw.onmessage = resolve; });
+        })
+      .then(e => {
+          // Verify the integrity of the array buffer sent back from
+          // ServiceWorker via Client.postMessage.
+          assert_equals(text_decoder.decode(e.data), message);
+          assert_equals(e.data.byteLength, message.length);
+          return new Promise(resolve => { sw.onmessage = resolve; });
+        })
+      .then(e => {
+          // Verify that the array buffer on ServiceWorker is neutered.
+          assert_equals(e.data.content, '');
+          assert_equals(e.data.byteLength, 0);
+        });
+  }, 'postMessage with dictionary a transferable ArrayBuffer between ServiceWorker and Client');
+
 </script>
diff --git a/service-workers/service-worker/resources/postmessage-dictionary-transferables-worker.js b/service-workers/service-worker/resources/postmessage-dictionary-transferables-worker.js
new file mode 100644
index 0000000..87a4500
--- /dev/null
+++ b/service-workers/service-worker/resources/postmessage-dictionary-transferables-worker.js
@@ -0,0 +1,24 @@
+var messageHandler = function(port, e) {
+    var text_decoder = new TextDecoder;
+    port.postMessage({
+      content: text_decoder.decode(e.data),
+      byteLength: e.data.byteLength
+    });
+
+    // Send back the array buffer via Client.postMessage.
+    port.postMessage(e.data, {transfer: [e.data.buffer]});
+
+    port.postMessage({
+      content: text_decoder.decode(e.data),
+      byteLength: e.data.byteLength
+    });
+};
+
+self.addEventListener('message', e => {
+    if (e.ports[0]) {
+      // Wait for messages sent via MessagePort.
+      e.ports[0].onmessage = messageHandler.bind(null, e.ports[0]);
+      return;
+    }
+    messageHandler(e.source, e);
+  });
diff --git a/subresource-integrity/idlharness.window.js b/subresource-integrity/idlharness.window.js
index d4a6efd..d2997e5 100644
--- a/subresource-integrity/idlharness.window.js
+++ b/subresource-integrity/idlharness.window.js
@@ -7,11 +7,11 @@
 
 idl_test(
   ['SRI'],
-  ['html', 'dom', 'cssom'],
+  ['html', 'cssom', 'dom'],
   idl_array => {
     idl_array.add_objects({
       HTMLScriptElement: ['document.createElement("script")'],
       HTMLLinkElement: ['document.createElement("link")'],
     });
-  },
-  'webappsec-subresource-integrity interfaces');
+  }
+);
diff --git a/svg/idlharness.window.js b/svg/idlharness.window.js
index d71807a..46e8aae 100644
--- a/svg/idlharness.window.js
+++ b/svg/idlharness.window.js
@@ -11,12 +11,12 @@
   'svg',
   'g',
   'defs',
-  'Desc',
+  'desc',
   'title',
   'symbol',
   'use',
   'image',
-  'Switch',
+  'switch',
   'style',
   'path',
   'rect',
@@ -49,8 +49,7 @@
   'animate',
   'set',
   'animateMotion',
-  'mPath',
-  'animateColor',
+  'mpath',
   'animateColor',
   'animateTransform',
   'font',
@@ -127,12 +126,12 @@
       SVGSVGElement: ['objects.svg'],
       SVGGElement: ['objects.g'],
       SVGDefsElement: ['objects.defs'],
-      SVGDescElement: ['objects.Desc'],
+      SVGDescElement: ['objects.desc'],
       SVGTitleElement: ['objects.title'],
       SVGSymbolElement: ['objects.symbol'],
       SVGUseElement: ['objects.use'],
       SVGImageElement: ['objects.image'],
-      SVGSwitchElement: ['objects.Switch'],
+      SVGSwitchElement: ['objects.switch'],
       SVGStyleElement: ['objects.style'],
       SVGPoint: ['objects.svg.createSVGPoint()'],
       SVGPointList: ['objects.polygon.points'],
@@ -193,7 +192,7 @@
       SVGAnimateElement: ['objects.animate'],
       SVGSetElement: ['objects.set'],
       SVGAnimateMotionElement: ['objects.animateMotion'],
-      SVGMPathElement: ['objects.mPath'],
+      SVGMPathElement: ['objects.mpath'],
       SVGAnimateColorElement: ['objects.animateColor'],
       SVGAnimateTransformElement: ['objects.animateTransform'],
       SVGFontElement: ['objects.font'],
@@ -232,6 +231,5 @@
       SVGFETileElement: ['objects.feTile'],
       SVGFETurbulenceElement: ['objects.feTurbulence']
     });
-  },
-  'SVG interfaces'
+  }
 );
diff --git a/svg/painting/reftests/markers-orient-001.svg b/svg/painting/reftests/markers-orient-001.svg
index 55c2394..190423f 100644
--- a/svg/painting/reftests/markers-orient-001.svg
+++ b/svg/painting/reftests/markers-orient-001.svg
@@ -30,6 +30,9 @@
     .label {
       font-size: 18px;
     }
+    #reference {
+      display: none;
+    }
   </style>
 
   <defs>
diff --git a/svg/painting/reftests/paint-order-001-ref.svg b/svg/painting/reftests/paint-order-001-ref.svg
index 6e1e016..7822a80 100644
--- a/svg/painting/reftests/paint-order-001-ref.svg
+++ b/svg/painting/reftests/paint-order-001-ref.svg
@@ -51,11 +51,11 @@
       <use xlink:href="#path"/>
     </g>
     <g transform="translate(240,220)">
-      <use xlink:href="#path"/>
+      <use xlink:href="#path" style="marker:none"/>
       <use xlink:href="#path" style="stroke:none"/>
     </g>
     <g transform="translate(360,220)">
-      <use xlink:href="#path"/>
+      <use xlink:href="#path" style="stroke:none"/>
       <use xlink:href="#path" style="marker:none"/>
     </g>
     <g transform="translate(120,320)">
@@ -63,10 +63,10 @@
     </g>
     <g transform="translate(240,320)">
       <use xlink:href="#path"/>
-      <use xlink:href="#path" style="stroke:none"/>
+      <use xlink:href="#path" style="stroke:none;marker:none"/>
     </g>
     <g transform="translate(360,320)">
-      <use xlink:href="#path"/>
+      <use xlink:href="#path" style="stroke:none"/>
       <use xlink:href="#path" style="marker:none"/>
     </g>
   </g>
diff --git a/tools/serve/serve.py b/tools/serve/serve.py
index 2cf41a4..56c6f92 100644
--- a/tools/serve/serve.py
+++ b/tools/serve/serve.py
@@ -154,6 +154,7 @@
 
 class HtmlWrapperHandler(WrapperHandler):
     global_type = None
+    headers = [('Content-Type', 'text/html')]
 
     def check_exposure(self, request):
         if self.global_type:
@@ -752,9 +753,11 @@
     computed_properties = ["ws_doc_root"] + config.ConfigBuilder.computed_properties
 
     def __init__(self, *args, **kwargs):
+        if "subdomains" not in kwargs:
+            kwargs["subdomains"] = _subdomains
+        if "not_subdomains" not in kwargs:
+            kwargs["not_subdomains"] = _not_subdomains
         super(ConfigBuilder, self).__init__(
-            subdomains=_subdomains,
-            not_subdomains=_not_subdomains,
             *args,
             **kwargs
         )
diff --git a/tools/serve/test_serve.py b/tools/serve/test_serve.py
index 149ed0e..e939c3a 100644
--- a/tools/serve/test_serve.py
+++ b/tools/serve/test_serve.py
@@ -14,22 +14,18 @@
 def test_make_hosts_file_nix():
     with ConfigBuilder(ports={"http": [8000]},
                        browser_host="foo.bar",
-                       alternate_hosts={"alt": "foo2.bar"}) as c:
+                       alternate_hosts={"alt": "foo2.bar"},
+                       subdomains={"a", "b"},
+                       not_subdomains={"x, y"}) as c:
         hosts = serve.make_hosts_file(c, "192.168.42.42")
         lines = hosts.split("\n")
         assert set(lines) == {"",
                               "192.168.42.42\tfoo.bar",
                               "192.168.42.42\tfoo2.bar",
-                              "192.168.42.42\twww.foo.bar",
-                              "192.168.42.42\twww.foo2.bar",
-                              "192.168.42.42\twww1.foo.bar",
-                              "192.168.42.42\twww1.foo2.bar",
-                              "192.168.42.42\twww2.foo.bar",
-                              "192.168.42.42\twww2.foo2.bar",
-                              "192.168.42.42\txn--lve-6lad.foo.bar",
-                              "192.168.42.42\txn--lve-6lad.foo2.bar",
-                              "192.168.42.42\txn--n8j6ds53lwwkrqhv28a.foo.bar",
-                              "192.168.42.42\txn--n8j6ds53lwwkrqhv28a.foo2.bar"}
+                              "192.168.42.42\ta.foo.bar",
+                              "192.168.42.42\ta.foo2.bar",
+                              "192.168.42.42\tb.foo.bar",
+                              "192.168.42.42\tb.foo2.bar"}
         assert lines[-1] == ""
 
 @pytest.mark.skipif(platform.uname()[0] != "Windows",
@@ -37,24 +33,22 @@
 def test_make_hosts_file_windows():
     with ConfigBuilder(ports={"http": [8000]},
                        browser_host="foo.bar",
-                       alternate_hosts={"alt": "foo2.bar"}) as c:
+                       alternate_hosts={"alt": "foo2.bar"},
+                       subdomains={"a", "b"},
+                       not_subdomains={"x, y"}) as c:
         hosts = serve.make_hosts_file(c, "192.168.42.42")
         lines = hosts.split("\n")
         assert set(lines) == {"",
-                              "0.0.0.0\tnonexistent.foo.bar",
-                              "0.0.0.0\tnonexistent.foo2.bar",
+                              "0.0.0.0\tx.foo.bar",
+                              "0.0.0.0\tx.foo2.bar",
+                              "0.0.0.0\ty.foo.bar",
+                              "0.0.0.0\ty.foo2.bar",
                               "192.168.42.42\tfoo.bar",
                               "192.168.42.42\tfoo2.bar",
-                              "192.168.42.42\twww.foo.bar",
-                              "192.168.42.42\twww.foo2.bar",
-                              "192.168.42.42\twww1.foo.bar",
-                              "192.168.42.42\twww1.foo2.bar",
-                              "192.168.42.42\twww2.foo.bar",
-                              "192.168.42.42\twww2.foo2.bar",
-                              "192.168.42.42\txn--lve-6lad.foo.bar",
-                              "192.168.42.42\txn--lve-6lad.foo2.bar",
-                              "192.168.42.42\txn--n8j6ds53lwwkrqhv28a.foo.bar",
-                              "192.168.42.42\txn--n8j6ds53lwwkrqhv28a.foo2.bar"}
+                              "192.168.42.42\ta.foo.bar",
+                              "192.168.42.42\ta.foo2.bar",
+                              "192.168.42.42\tb.foo.bar",
+                              "192.168.42.42\tb.foo2.bar"}
         assert lines[-1] == ""
 
 
diff --git a/tools/wptserve/setup.py b/tools/wptserve/setup.py
index 194c337..c9c364b 100644
--- a/tools/wptserve/setup.py
+++ b/tools/wptserve/setup.py
@@ -1,7 +1,7 @@
 from setuptools import setup
 
 PACKAGE_VERSION = '2.0'
-deps = ["six>=1.8"]
+deps = ["six>=1.8", "h2==3.0.1"]
 
 setup(name='wptserve',
       version=PACKAGE_VERSION,
diff --git a/touch-events/idlharness.window.js b/touch-events/idlharness.window.js
index dbea07a..f88c9aa 100644
--- a/touch-events/idlharness.window.js
+++ b/touch-events/idlharness.window.js
@@ -15,6 +15,5 @@
       Touch: ['new Touch({identifier: 1, target: document})'],
       TouchEvent: ['new TouchEvent("name")'],
     });
-  },
-  'Test IDL implementation of touch-events API'
+  }
 );
diff --git a/trusted-types/HTMLAnchorElement-href.tentative.html b/trusted-types/HTMLAnchorElement-href.tentative.html
deleted file mode 100644
index 33c8989..0000000
--- a/trusted-types/HTMLAnchorElement-href.tentative.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.sub.js"></script>
-<body>
-<script>
-  //helper function for the tests
-  function testHref(str, url) {
-    var a = document.createElement('a');
-    a.href = url;
-    assert_equals(a.href, str);
-  }
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "a.href = URLS.safe, TrustedURL.create");
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "a.href = URLS.safe, TrustedURL.unsafelyCreate");
-</script>
diff --git a/trusted-types/HTMLAreaElement-href.tentative.html b/trusted-types/HTMLAreaElement-href.tentative.html
deleted file mode 100644
index 8ac509d..0000000
--- a/trusted-types/HTMLAreaElement-href.tentative.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.sub.js"></script>
-<body>
-  <map>
-    <script>
-    //helper function for the tests
-    function testHref(str, url) {
-      var area = document.createElement('area');
-      area.href = url;
-      area.alt = "Area";
-      assert_equals(area.href, str);
-    }
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.create(URLS.safe));
-    }, "area.href = URLS.safe, TrustedURL.create");
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-    }, "area.href = URLS.safe, TrustedURL.unsafelyCreate");
-    </script>
-  </map>
-</body>
diff --git a/trusted-types/HTMLBaseElement-href.tentative.html b/trusted-types/HTMLBaseElement-href.tentative.html
deleted file mode 100644
index 80bed80..0000000
--- a/trusted-types/HTMLBaseElement-href.tentative.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <script>
-    //helper function for the tests
-    function testHref(str, url) {
-      var base = document.createElement('base');
-      base.href = url;
-      assert_equals(base.href, str);
-    }
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.create(URLS.safe));
-    }, "base.href = URLS.safe, TrustedURL.create");
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-    }, "base.href = URLS.safe, TrustedURL.unsafelyCreate");
-  </script>
-</head>
diff --git a/trusted-types/HTMLElement-generic.tentative.html b/trusted-types/HTMLElement-generic.tentative.html
new file mode 100644
index 0000000..486b008
--- /dev/null
+++ b/trusted-types/HTMLElement-generic.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/helper.sub.js"></script>
+</head>
+<body>
+<script>
+  //TrustedURL Assignments
+  let testCases = [
+    [ 'a', 'href' ],
+    [ 'area', 'href' ],
+    [ 'base', 'href' ],
+    [ 'frame', 'src' ],
+    [ 'iframe', 'src' ],
+    [ 'img', 'src' ],
+    [ 'input', 'src' ],
+    [ 'link', 'href' ],
+    [ 'video', 'src' ],
+    [ 'object', 'data' ],
+    [ 'object', 'codeBase' ],
+    [ 'source', 'src' ],
+    [ 'track', 'src' ]
+  ];
+
+  testCases.forEach(c => {
+    test(t => {
+      assert_accepts_trusted_url(c[0], c[1]);
+    }, c[0] + "." + c[1] + " accepts TrustedURL");
+  });
+
+  //TrustedScriptURL Assignments
+  let scriptTestCases = [
+    [ 'embed', 'src' ],
+    [ 'script', 'src' ]
+  ];
+
+  scriptTestCases.forEach(c => {
+    test(t => {
+      assert_accepts_trusted_script_url(c[0], c[1]);
+    }, c[0] + "." + c[1] + " accepts TrustedScriptURL");
+  });
+</script>
diff --git a/trusted-types/HTMLIFrameElement-src.tentative.html b/trusted-types/HTMLIFrameElement-src.tentative.html
deleted file mode 100644
index 6e89a0f..0000000
--- a/trusted-types/HTMLIFrameElement-src.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('iframe');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('iframe');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/trusted-types/HTMLImageElement-src.tentative.html b/trusted-types/HTMLImageElement-src.tentative.html
deleted file mode 100644
index 5797aa5..0000000
--- a/trusted-types/HTMLImageElement-src.tentative.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('image');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('image');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
-
diff --git a/trusted-types/HTMLLinkElement-href.tentative.html b/trusted-types/HTMLLinkElement-href.tentative.html
deleted file mode 100644
index 7f6e7e2..0000000
--- a/trusted-types/HTMLLinkElement-href.tentative.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.sub.js"></script>
-<body>
-<script>
-  //helper function for the tests
-  function testHref(str, url) {
-    var link = document.createElement('link');
-    link.href = url;
-    assert_equals(link.href, str);
-  }
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "link.href = URLS.safe, TrustedURL.create");
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "link.href = URLS.safe, TrustedURL.unsafelyCreate");
-</script>
diff --git a/trusted-types/HTMLMediaElement-src.tentative.html b/trusted-types/HTMLMediaElement-src.tentative.html
deleted file mode 100644
index c54dfdd..0000000
--- a/trusted-types/HTMLMediaElement-src.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('video');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('video');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/trusted-types/HTMLObjectElement.tentative.html b/trusted-types/HTMLObjectElement.tentative.html
deleted file mode 100644
index a9dcc94..0000000
--- a/trusted-types/HTMLObjectElement.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.sub.js"></script>
-<body>
-<script>
-  //helper function for the tests
-  function testData(str, url) {
-    var objectElement = document.createElement('object');
-    objectElement.data = url;
-    objectElement.codeBase = url;
-    assert_equals(objectElement.data, str);
-    assert_equals(objectElement.codeBase,str);
-  }
-
-  test(t => {
-    testData(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "Basic processing: safe URL, safe construction.");
-
-  test(t => {
-    testData(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "Basic processing: safe URL, unsafe construction.");
-</script>
diff --git a/trusted-types/HTMLSourceElement-src.tentative.html b/trusted-types/HTMLSourceElement-src.tentative.html
deleted file mode 100644
index 6791a0f..0000000
--- a/trusted-types/HTMLSourceElement-src.tentative.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('source');
-    d.src = url;
-    assert_equals(d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('source');
-    d.src = url;
-    assert_equals(d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLAnchorElement-href.tentative.html b/trusted-types/block-string-assignment-to-HTMLAnchorElement-href.tentative.html
deleted file mode 100644
index 53334e2..0000000
--- a/trusted-types/block-string-assignment-to-HTMLAnchorElement-href.tentative.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-<script>
-  //helper function for the tests
-  function testHref(str, url) {
-    var a = document.createElement('a');
-    a.href = url;
-    assert_equals(a.href, str);
-  }
-
-  //URL assignments do not throw.
-  test(t => {
-    testHref(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "a.href = URLS.safe, TrustedURL.create");
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "a.href = URLS.safe, TrustedURL.unsafelyCreate");
-
-  // String assignments throw.
-  test(t => {
-    var a = document.createElement('a');
-    assert_throws(new TypeError(), _ => {
-      a.href = "A string";
-    });
-  }, "`a.href = string` throws");
-
-  //Null assignment throws.
-  test(t => {
-    var a = document.createElement('a');
-    assert_throws(new TypeError(), _ => {
-      a.href = null;
-    });
-  }, "`a.href = null` throws");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLAreaElement-href.tentative.html b/trusted-types/block-string-assignment-to-HTMLAreaElement-href.tentative.html
deleted file mode 100644
index 5879d1c..0000000
--- a/trusted-types/block-string-assignment-to-HTMLAreaElement-href.tentative.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-  <map>
-    <script>
-    //helper function for the tests
-    function testHref(str, url) {
-      var area = document.createElement('area');
-      area.href = url;
-      area.alt = "Area";
-      assert_equals(area.href, str);
-    }
-
-    //URL assignments do not throw.
-    test(t => {
-      testHref(URLS.safe, TrustedURL.create(URLS.safe));
-    }, "area.href = URLS.safe, TrustedURL.create");
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-    }, "area.href = URLS.safe, TrustedURL.unsafelyCreate");
-
-    // String assignments throw.
-    test(t => {
-      var area = document.createElement('area');
-      assert_throws(new TypeError(), _ => {
-        area.href = "A string";
-      });
-    }, "`area.href = string` throws");
-
-    //Null assignment throws.
-    test(t => {
-      var area = document.createElement('area');
-      assert_throws(new TypeError(), _ => {
-        area.href = null;
-      });
-    }, "`area.href = null` throws");
-    </script>
-  </map>
diff --git a/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html b/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html
deleted file mode 100644
index a973e29..0000000
--- a/trusted-types/block-string-assignment-to-HTMLBaseElement-href.tentative.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-
-  <script>
-    //helper function for the tests
-    function testHref(str, url) {
-      var base = document.createElement('base');
-      base.href = url;
-      assert_equals(base.href, str);
-    }
-
-    //URL assignments do not throw.
-    test(t => {
-      testHref(URLS.safe, TrustedURL.create(URLS.safe));
-    }, "base.href = URLS.safe, TrustedURL.create");
-
-    test(t => {
-      testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-    }, "base.href = URLS.safe, TrustedURL.unsafelyCreate");
-
-    // String assignments throw.
-    test(t => {
-      var base = document.createElement('base');
-      assert_throws(new TypeError(), _ => {
-        base.href = "A string";
-      });
-    }, "`base.href = string` throws");
-
-    //Null assignment throws.
-    test(t => {
-      var base = document.createElement('base');
-      assert_throws(new TypeError(), _ => {
-        base.href = null;
-      });
-    }, "`base.href = null` throws");
-  </script>
-</head>
diff --git a/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html b/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html
new file mode 100644
index 0000000..79bbb24
--- /dev/null
+++ b/trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/helper.sub.js"></script>
+
+  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+</head>
+<body>
+<script>
+  //TrustedURL Assignments
+  let testCases = [
+    [ 'a', 'href' ],
+    [ 'area', 'href' ],
+    [ 'base', 'href' ],
+    [ 'frame', 'src' ],
+    [ 'iframe', 'src' ],
+    [ 'img', 'src' ],
+    [ 'input', 'src' ],
+    [ 'link', 'href' ],
+    [ 'video', 'src' ],
+    [ 'object', 'data' ],
+    [ 'object', 'codeBase' ],
+    [ 'source', 'src' ],
+    [ 'track', 'src' ]
+  ];
+
+  testCases.forEach(c => {
+    test(t => {
+      assert_accepts_trusted_url(c[0], c[1]);
+      assert_throws_no_trusted_type(c[0], c[1], 'A string');
+      assert_throws_no_trusted_type(c[0], c[1], null);
+    }, c[0] + "." + c[1] + " accepts TrustedURL");
+  });
+
+  //TrustedScriptURL Assignments
+  let scriptTestCases = [
+    [ 'embed', 'src' ],
+    [ 'script', 'src' ]
+  ];
+
+  scriptTestCases.forEach(c => {
+    test(t => {
+      assert_accepts_trusted_script_url(c[0], c[1]);
+      assert_throws_no_trusted_type(c[0], c[1], 'A string');
+      assert_throws_no_trusted_type(c[0], c[1], null);
+    }, c[0] + "." + c[1] + " accepts TrustedScriptURL");
+  });
+</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html b/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html
deleted file mode 100644
index 8db0d60..0000000
--- a/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('iframe');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('iframe');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('iframe');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html b/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html
deleted file mode 100644
index 4446106..0000000
--- a/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('img');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('img');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('img');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html b/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html
deleted file mode 100644
index 11950da..0000000
--- a/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-<script>
-  //helper function for the tests
-  function testHref(str, url) {
-    var link = document.createElement('link');
-    link.href = url;
-    assert_equals(link.href, str);
-  }
-
-  //URL assignments do not throw.
-  test(t => {
-    testHref(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "link.href = URLS.safe, TrustedURL.create");
-
-  test(t => {
-    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "link.href = URLS.safe, TrustedURL.unsafelyCreate");
-
-  // String assignments throw.
-  test(t => {
-    var link = document.createElement('link');
-    assert_throws(new TypeError(), _ => {
-      link.href = "A string";
-    });
-  }, "`link.href = string` throws");
-
-  //Null assignment throws.
-  test(t => {
-    var link = document.createElement('link');
-    assert_throws(new TypeError(), _ => {
-      link.href = null;
-    });
-  }, "`link.href = null` throws");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html b/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html
deleted file mode 100644
index 38ac53f..0000000
--- a/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('video');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('video');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('video');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html b/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html
deleted file mode 100644
index 87e1f46..0000000
--- a/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-<script>
-  //helper function for the tests
-  function testData(str, url) {
-    var objectElement = document.createElement('object');
-    objectElement.data = url;
-    objectElement.codeBase = url;
-    assert_equals(objectElement.data, str);
-    assert_equals(objectElement.codeBase, str);
-  }
-
-  //URL assignments do not throw
-  test(t => {
-    testData(URLS.safe, TrustedURL.create(URLS.safe));
-  }, "Basic processing: safe URL, safe construction.");
-
-  test(t => {
-    testData(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
-  }, "Basic processing: safe URL, unsafe construction.");
-
-  //String assignments throw
-  test(t => {
-    var objectElement = document.createElement('object');
-    assert_throws(new TypeError(), _ => {
-      objectElement.data = "A string";
-    });
-  }, "`objectElement.data = string` throws");
-
-  test(t => {
-    var objectElement = document.createElement('object');
-    assert_throws(new TypeError(), _ => {
-      objectElement.codeBase = "A string";
-    });
-  }, "`objectElement.codeBase = string` throws");
-
-  //Null assignment throws.
-  test(t => {
-    var objectElement = document.createElement('object');
-    assert_throws(new TypeError(), _ => {
-      objectElement.data = null;
-    });
-  }, "`objectElement.data = null` throws");
-
-  //Null assignment throws.
-  test(t => {
-    var objectElement = document.createElement('object');
-    assert_throws(new TypeError(), _ => {
-      objectElement.codeBase = null;
-    });
-  }, "`objectElement.codeBase = null` throws");
-</script>
diff --git a/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html b/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html
deleted file mode 100644
index 37ab90c..0000000
--- a/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('source');
-    d.src = url;
-    assert_equals(d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('source');
-    d.src = url;
-    assert_equals(d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var url = "Fail."
-
-    var d = document.createElement('source');
-    assert_throws(new TypeError(), _ => {
-      d.src = url;
-    });
-  }, "'src = string' throws.");
-</script>
-
diff --git a/trusted-types/block-string-assignment-to-embed-src.tentative.html b/trusted-types/block-string-assignment-to-embed-src.tentative.html
deleted file mode 100644
index 005dfe2..0000000
--- a/trusted-types/block-string-assignment-to-embed-src.tentative.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="./support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-<script>
-  // String assignments throw.
-  test(t => {
-    var s = document.createElement('embed');
-    assert_throws(new TypeError(), _ => {
-      s.src = "Fail.";
-    });
-    assert_equals('', s.src);
-  }, "src = 'string' assignment throws.");
-
-  // TrustedURL assignments throw.
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('embed');
-    assert_throws(new TypeError(), _ => {
-      s.src = url;
-    });
-    assert_equals('', s.src);
-  }, "src = TrustedURL.unsafelyCreate(URLS.safe) assignment throws");
-
-  // TrustedScriptURL assignments work.
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.safe)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.javascript);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.javascript)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.external);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.external)");
-</script>
diff --git a/trusted-types/block-string-assignment-to-frame-src.tentative.html b/trusted-types/block-string-assignment-to-frame-src.tentative.html
deleted file mode 100644
index c915e43..0000000
--- a/trusted-types/block-string-assignment-to-frame-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('frame');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('frame');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('frame');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/block-string-assignment-to-input-src.tentative.html b/trusted-types/block-string-assignment-to-input-src.tentative.html
deleted file mode 100644
index 732ebe8..0000000
--- a/trusted-types/block-string-assignment-to-input-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('input');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('input');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('input');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/block-string-assignment-to-script-src.tentative.html b/trusted-types/block-string-assignment-to-script-src.tentative.html
deleted file mode 100644
index ade7684..0000000
--- a/trusted-types/block-string-assignment-to-script-src.tentative.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-  <script src="support/helper.sub.js"></script>
-
-  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-</head>
-<body>
-<script>
-  // String assignments throw.
-  test(t => {
-    var s = document.createElement('script');
-    assert_throws(new TypeError(), _ => {
-      s.src = URLS.safe;
-    });
-    assert_equals('', s.src);
-  }, "'string'");
-
-  // TrustedURL assignments throw.
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('script');
-    assert_throws(new TypeError(), _ => {
-      s.src = url;
-    });
-    assert_equals('', s.src);
-  }, "TrustedURL(safe)");
-
-  // TrustedScriptURL assignments work.
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "TrustedScriptURL(safe)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.javascript);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "TrustedScriptURL(javascript)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.external);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "TrustedScriptURL(external)");
-</script>
diff --git a/trusted-types/block-string-assignment-to-track-src.tentative.html b/trusted-types/block-string-assignment-to-track-src.tentative.html
deleted file mode 100644
index b6f81e9..0000000
--- a/trusted-types/block-string-assignment-to-track-src.tentative.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
-<body>
-<script>
-  //URL assignments don't throw
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('track');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('track');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-
-  //String assignment throws
-  test(t => {
-    var d = document.createElement('track');
-    assert_throws(new TypeError(), _ => {
-      d.src = "Fail.";
-    });
-  }, "'src = string' throws.");
-</script>
diff --git a/trusted-types/embed-src.tentative.html b/trusted-types/embed-src.tentative.html
deleted file mode 100644
index 7576027..0000000
--- a/trusted-types/embed-src.tentative.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-<body>
-<script>
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.safe)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.javascript);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.javascript)");
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.external);
-
-    var s = document.createElement('embed');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  }, "src = TrustedScriptURL.unsafelyCreate(URLS.external)");
-</script>
diff --git a/trusted-types/frame-src.tentative.html b/trusted-types/frame-src.tentative.html
deleted file mode 100644
index 31a1ce9..0000000
--- a/trusted-types/frame-src.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('frame');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('frame');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/trusted-types/input-src.tentative.html b/trusted-types/input-src.tentative.html
deleted file mode 100644
index 8cbd0ad..0000000
--- a/trusted-types/input-src.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('input');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('input');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/trusted-types/script-src.tentative.html b/trusted-types/script-src.tentative.html
deleted file mode 100644
index 7235cc3..0000000
--- a/trusted-types/script-src.tentative.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/helper.sub.js"></script>
-<body>
-<script>
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.safe);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  });
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.javascript);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  });
-
-  test(t => {
-    var url = TrustedScriptURL.unsafelyCreate(URLS.external);
-
-    var s = document.createElement('script');
-    s.src = url;
-    assert_equals(url + '', s.src);
-  });
-</script>
diff --git a/trusted-types/support/helper.sub.js b/trusted-types/support/helper.sub.js
index 036dbed..91112d8 100644
--- a/trusted-types/support/helper.sub.js
+++ b/trusted-types/support/helper.sub.js
@@ -45,3 +45,31 @@
     document.body.appendChild(i);
   });
 }
+
+let trustedHTML = TrustedHTML.escape(STRINGS.unescapedHTML);
+function assert_accepts_trusted_html(tag, attribute) {
+  let elem = document.createElement(tag);
+  elem[attribute] = trustedHTML;
+  assert_equals(elem[attribute] + "", STRINGS.unescapedHTML);
+}
+
+let trustedURL = TrustedURL.create(URLS.safe);
+function assert_accepts_trusted_url(tag, attribute) {
+  let elem = document.createElement(tag);
+  elem[attribute] = trustedURL;
+  assert_equals(elem[attribute] + "", URLS.safe);
+}
+
+let trustedScriptURL = TrustedScriptURL.unsafelyCreate(URLS.safe);
+function assert_accepts_trusted_script_url(tag, attribute) {
+  let elem = document.createElement(tag);
+  elem[attribute] = trustedScriptURL;
+  assert_equals(elem[attribute] + "", URLS.safe);
+}
+
+function assert_throws_no_trusted_type(tag, attribute, value) {
+  let elem = document.createElement(tag);
+  assert_throws(new TypeError(), _ => {
+    elem[attribute] = value;
+  });
+}
diff --git a/trusted-types/track-src.tentative.html b/trusted-types/track-src.tentative.html
deleted file mode 100644
index cfe5374..0000000
--- a/trusted-types/track-src.tentative.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="./support/helper.sub.js"></script>
-
-<body>
-<script>
-  test(t => {
-    var url = TrustedURL.create(URLS.safe);
-
-    var d = document.createElement('track');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.create().");
-
-  test(t => {
-    var url = TrustedURL.unsafelyCreate(URLS.safe);
-
-    var d = document.createElement('track');
-    d.src = url;
-    assert_equals("" + d.src, URLS.safe);
-  }, "src = TrustedURL.unsafelyCreate().");
-</script>
diff --git a/uievents/idlharness.window.js b/uievents/idlharness.window.js
new file mode 100644
index 0000000..928c8c9
--- /dev/null
+++ b/uievents/idlharness.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+idl_test(
+  ['uievents'],
+  ['dom'],
+  idl_array => {
+    idl_array.add_objects({
+      FocusEvent: ['new FocusEvent("event")'],
+      MouseEvent: ['new MouseEvent("event")'],
+      WheelEvent: ['new WheelEvent("event")'],
+      KeyboardEvent: ['new KeyboardEvent("event")'],
+      CompositionEvent: ['new CompositionEvent("event")'],
+      UIEvent: ['new UIEvent("event")'],
+      InputEvent: ['new InputEvent("event")'],
+    });
+  }
+);
diff --git a/uievents/interfaces.html b/uievents/interfaces.html
deleted file mode 100644
index 0d20f1d..0000000
--- a/uievents/interfaces.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>UI Events IDL tests</title>
-<meta name=timeout content=long>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=/resources/WebIDLParser.js></script>
-<script src=/resources/idlharness.js></script>
-
-<h1>UI Events IDL tests</h1>
-<div id=log></div>
-
-<script>
-"use strict";
-function doTest([dom, uievents]) {
-  var idlArray = new IdlArray();
-  idlArray.add_untested_idls(dom);
-  idlArray.add_idls(uievents);
-
-  idlArray.add_objects({
-    FocusEvent: ['new FocusEvent("event")'],
-    MouseEvent: ['new MouseEvent("event")'],
-    WheelEvent: ['new WheelEvent("event")'],
-    KeyboardEvent: ['new KeyboardEvent("event")'],
-    CompositionEvent: ['new CompositionEvent("event")'],
-    UIEvent: ['new UIEvent("event")'],
-    InputEvent: ['new InputEvent("event")'],
-  });
-  idlArray.test();
-};
-
-function fetchData(url) {
-  return fetch(url).then((response) => response.text());
-}
-
-function waitForLoad() {
-  return new Promise(function(resolve) {
-    addEventListener("load", resolve);
-  });
-}
-
-promise_test(function() {
-  return Promise.all([fetchData("/interfaces/dom.idl"),
-                      fetchData("/interfaces/uievents.idl")])
-                .then(doTest);
-}, "Test driver");
-
-</script>
diff --git a/user-timing/idlharness.any.js b/user-timing/idlharness.any.js
index 6c60cb5..d1cb9b5 100644
--- a/user-timing/idlharness.any.js
+++ b/user-timing/idlharness.any.js
@@ -28,5 +28,5 @@
       PerformanceMark: ['mark'],
       PerformanceMeasure: ['measure'],
     });
-  },
-  'Test IDL implementation of user-timing API');
+  }
+);
diff --git a/user-timing/measure.html b/user-timing/measure.html
index 8f2a618..d114ecd 100644
--- a/user-timing/measure.html
+++ b/user-timing/measure.html
@@ -76,7 +76,7 @@
 
         function onload_test()
         {
-            // test for existance of User Timing and Performance Timeline interface
+            // test for existence of User Timing and Performance Timeline interface
             if (!has_required_interfaces())
             {
                 test_true(false,
diff --git a/user-timing/measure_navigation_timing.html b/user-timing/measure_navigation_timing.html
index fa472fd..d6480d2 100644
--- a/user-timing/measure_navigation_timing.html
+++ b/user-timing/measure_navigation_timing.html
@@ -73,7 +73,7 @@
 
         function onload_test()
         {
-            // test for existance of User Timing and Performance Timeline interface
+            // test for existence of User Timing and Performance Timeline interface
             if (!has_required_interfaces())
             {
                 test_true(false,
diff --git a/vibration/idl.html b/vibration/idl.html
deleted file mode 100644
index 167090c..0000000
--- a/vibration/idl.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>IDL harness tests for Vibration API</title>
-<body>
-    <h1>Description</h1>
-    <p>
-      This test validates the IDL defined by the Vibration API.
-    </p>
-    <p>
-      This test uses <a href="/resources/idlharness.js">idlharness.js</a>
-    </p>
-<div id="log"></div>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=/resources/WebIDLParser.js></script>
-<script src=/resources/idlharness.js></script>
-<script>
-var idl_array = new IdlArray();
-idl_array.add_untested_idls("interface Navigator {};");
-idl_array.add_idls("partial interface Navigator { boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);};");
-idl_array.add_objects({Navigator: ['navigator']});
-idl_array.test();
-</script>
diff --git a/vibration/idlharness.window.js b/vibration/idlharness.window.js
new file mode 100644
index 0000000..fbe14da
--- /dev/null
+++ b/vibration/idlharness.window.js
@@ -0,0 +1,12 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+idl_test(
+  ['vibration'],
+  ['html'],
+  idl_array => {
+    idl_array.add_objects({Navigator: ['navigator']});
+  }
+);
diff --git a/web-locks/META.yml b/web-locks/META.yml
index 39acca8..24e7d28 100644
--- a/web-locks/META.yml
+++ b/web-locks/META.yml
@@ -1,4 +1,4 @@
-spec: https://inexorabletash.github.io/web-locks/
+spec: https://wicg.github.io/web-locks/
 suggested_reviewers:
   - inexorabletash
   - pwnall
diff --git a/web-locks/README.md b/web-locks/README.md
index e8091f8..8ff534e 100644
--- a/web-locks/README.md
+++ b/web-locks/README.md
@@ -1,5 +1,5 @@
 This directory contains a test suite for the proposed Web Locks API.
 
-Explainer: https://github.com/inexorabletash/web-locks
+Explainer: https://github.com/WICG/web-locks/
 
-Spec: https://inexorabletash.github.io/web-locks/
+Spec: https://wicg.github.io/web-locks/
diff --git a/web-locks/acquire.tentative.https.html b/web-locks/acquire.tentative.https.any.js
similarity index 92%
rename from web-locks/acquire.tentative.https.html
rename to web-locks/acquire.tentative.https.any.js
index 3c360ab..be0e837 100644
--- a/web-locks/acquire.tentative.https.html
+++ b/web-locks/acquire.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: navigator.locks.request method</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: navigator.locks.request method
+// META: script=resources/helpers.js
+
 'use strict';
 
 promise_test(async t => {
@@ -126,5 +121,3 @@
   assert_equals(Promise.resolve(p), p, 'request() result is a Promise');
   await promise_rejects(t, test_error, p, 'result should reject');
 }, 'Returned Promise rejects if callback throws asynchronously');
-
-</script>
diff --git a/web-locks/clientids.tentative.https.html b/web-locks/clientids.tentative.https.html
index 8a99d7d..9ce4c4e 100644
--- a/web-locks/clientids.tentative.https.html
+++ b/web-locks/clientids.tentative.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web Locks API: Client IDs in query() vs. Service Worker</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
+<link rel=help href="https://wicg.github.io/web-locks/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
diff --git a/web-locks/frames.tentative.https.html b/web-locks/frames.tentative.https.html
index 366a7ea..7d8383c 100644
--- a/web-locks/frames.tentative.https.html
+++ b/web-locks/frames.tentative.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web Locks API: Frames</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
+<link rel=help href="https://wicg.github.io/web-locks/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/helpers.js"></script>
diff --git a/web-locks/held.tentative.https.html b/web-locks/held.tentative.https.any.js
similarity index 87%
rename from web-locks/held.tentative.https.html
rename to web-locks/held.tentative.https.any.js
index ccf7c0b..151e3b3 100644
--- a/web-locks/held.tentative.https.html
+++ b/web-locks/held.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Lock held until callback result resolves</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: Lock held until callback result resolves
+// META: script=resources/helpers.js
+
 'use strict';
 
 // For uncaught rejections.
@@ -93,5 +88,3 @@
   });
   assert_true(callback_called, 'callback should have executed');
 }, 'held lock prevents the same client from acquiring it');
-
-</script>
diff --git a/web-locks/ifAvailable.tentative.https.html b/web-locks/ifAvailable.tentative.https.any.js
similarity index 94%
rename from web-locks/ifAvailable.tentative.https.html
rename to web-locks/ifAvailable.tentative.https.any.js
index ee190b2..a63caf6 100644
--- a/web-locks/ifAvailable.tentative.https.html
+++ b/web-locks/ifAvailable.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: ifAvailable option</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: ifAvailable option
+// META: script=resources/helpers.js
+
 'use strict';
 
 promise_test(async t => {
@@ -165,5 +160,3 @@
   });
   assert_true(callback1_called, 'callback1 should be called');
 }, 'Locks are available once previous release is processed');
-
-</script>
diff --git a/web-locks/interfaces-serviceworker.tentative.https.html b/web-locks/interfaces-serviceworker.tentative.https.html
index ea2a3d7..ca4d768 100644
--- a/web-locks/interfaces-serviceworker.tentative.https.html
+++ b/web-locks/interfaces-serviceworker.tentative.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web Locks API: WebIDL tests in service worker</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
+<link rel=help href="https://wicg.github.io/web-locks/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
diff --git a/web-locks/lock-attributes.tentative.https.html b/web-locks/lock-attributes.tentative.https.any.js
similarity index 64%
rename from web-locks/lock-attributes.tentative.https.html
rename to web-locks/lock-attributes.tentative.https.any.js
index e0c1c5b..e94fc95 100644
--- a/web-locks/lock-attributes.tentative.https.html
+++ b/web-locks/lock-attributes.tentative.https.any.js
@@ -1,10 +1,5 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Lock Attributes</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
+// META: title=Web Locks API: Lock Attributes
+
 'use strict';
 
 promise_test(async t => {
@@ -20,5 +15,3 @@
     assert_equals(lock.mode, 'shared');
   });
 }, 'Lock attributes reflect requested properties (shared)');
-
-</script>
diff --git a/web-locks/mode-exclusive.tentative.https.html b/web-locks/mode-exclusive.tentative.https.any.js
similarity index 76%
rename from web-locks/mode-exclusive.tentative.https.html
rename to web-locks/mode-exclusive.tentative.https.any.js
index 7769ca5..9140462 100644
--- a/web-locks/mode-exclusive.tentative.https.html
+++ b/web-locks/mode-exclusive.tentative.https.any.js
@@ -1,10 +1,5 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Exclusive Mode</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
+// META: title=Web Locks API: Exclusive Mode
+
 'use strict';
 
 promise_test(async t => {
@@ -36,5 +31,3 @@
   await inner_promise;
   assert_array_equals(granted, [2, 1]);
 }, 'Requests for distinct resources can be granted');
-
-</script>
diff --git a/web-locks/mode-mixed.tentative.https.html b/web-locks/mode-mixed.tentative.https.any.js
similarity index 82%
rename from web-locks/mode-mixed.tentative.https.html
rename to web-locks/mode-mixed.tentative.https.any.js
index ac8f365..3b30494 100644
--- a/web-locks/mode-mixed.tentative.https.html
+++ b/web-locks/mode-mixed.tentative.https.any.js
@@ -1,10 +1,5 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Mixed Modes</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
+// META: title=Web Locks API: Mixed Modes
+
 'use strict';
 
 promise_test(async t => {
@@ -47,5 +42,3 @@
     ['a-shared-1', 'a-shared-2', 'a-shared-3', 'b-exclusive', 'a-exclusive']);
 
 }, 'Lock requests are granted in order');
-
-</script>
diff --git a/web-locks/mode-shared.tentative.https.html b/web-locks/mode-shared.tentative.https.any.js
similarity index 81%
rename from web-locks/mode-shared.tentative.https.html
rename to web-locks/mode-shared.tentative.https.any.js
index cac3a57..7c8a844 100644
--- a/web-locks/mode-shared.tentative.https.html
+++ b/web-locks/mode-shared.tentative.https.any.js
@@ -1,10 +1,5 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Shared Mode</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
+// META: title=Web Locks API: Shared Mode
+
 'use strict';
 
 promise_test(async t => {
@@ -40,5 +35,3 @@
   assert_true(a_acquired, 'first lock acquired');
   assert_true(a_acquired_again, 'second lock acquired');
 }, 'Shared locks are not exclusive');
-
-</script>
diff --git a/web-locks/non-secure-context.tentative.any.js b/web-locks/non-secure-context.tentative.any.js
new file mode 100644
index 0000000..e9726e6
--- /dev/null
+++ b/web-locks/non-secure-context.tentative.any.js
@@ -0,0 +1,13 @@
+// META title=Web Locks API: API not available in non-secure context
+
+'use strict';
+
+test(t => {
+  assert_false(self.isSecureContext);
+  assert_false('locks' in navigator,
+               'navigator.locks is only present in secure contexts');
+  assert_false('LockManager' in self,
+               'LockManager is only present in secure contexts');
+  assert_false('Lock' in self,
+               'Lock interface is only present in secure contexts');
+}, 'API presence in non-secure contexts');
diff --git a/web-locks/non-secure-context.tentative.html b/web-locks/non-secure-context.tentative.html
deleted file mode 100644
index 328e451..0000000
--- a/web-locks/non-secure-context.tentative.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: API not available in non-secure context</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-'use strict';
-
-test(t => {
-  assert_false(window.isSecureContext);
-  assert_false('locks' in navigator,
-               'navigator.locks is only present in secure contexts');
-  assert_false('LockManager' in self,
-               'LockManager is only present in secure contexts');
-  assert_false('Lock' in self,
-               'Lock interface is only present in secure contexts');
-}, 'API presence in non-secure contexts');
-</script>
diff --git a/web-locks/opaque-origin.tentative.https.html b/web-locks/opaque-origin.tentative.https.html
index 7abae82..589c2a7 100644
--- a/web-locks/opaque-origin.tentative.https.html
+++ b/web-locks/opaque-origin.tentative.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web Locks API: Opaque origins</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
+<link rel=help href="https://wicg.github.io/web-locks/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
diff --git a/web-locks/query-empty.tentative.https.html b/web-locks/query-empty.tentative.https.any.js
similarity index 63%
rename from web-locks/query-empty.tentative.https.html
rename to web-locks/query-empty.tentative.https.any.js
index a613321..fe7e0bb 100644
--- a/web-locks/query-empty.tentative.https.html
+++ b/web-locks/query-empty.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: navigator.locks.query method - no locks held</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: navigator.locks.query method - no locks held
+// META: script=resources/helpers.js
+
 'use strict';
 
 promise_test(async t => {
@@ -20,5 +15,3 @@
   assert_true(Array.isArray(state.held), 'State `held` property is an array');
   assert_array_equals(state.held, [], 'Held array is empty');
 }, 'query() returns dictionary with empty arrays when no locks are held');
-
-</script>
diff --git a/web-locks/query-order.tentative.https.html b/web-locks/query-order.tentative.https.any.js
similarity index 90%
rename from web-locks/query-order.tentative.https.html
rename to web-locks/query-order.tentative.https.any.js
index 25ac10a..1810a96 100644
--- a/web-locks/query-order.tentative.https.html
+++ b/web-locks/query-order.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: navigator.locks.query ordering</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: navigator.locks.query ordering
+// META: script=resources/helpers.js
+
 'use strict';
 
 // Grab a lock and hold until a release function is called. Resolves
@@ -112,5 +107,3 @@
   assert_array_equals(relevant_held_names, [res3, res1, res2],
                       'Held locks should appear in granted order.');
 }, 'Held locks appear in state in order granted, including when stolen');
-
-</script>
diff --git a/web-locks/query.tentative.https.html b/web-locks/query.tentative.https.any.js
similarity index 95%
rename from web-locks/query.tentative.https.html
rename to web-locks/query.tentative.https.any.js
index 77d37f3..14fdeca 100644
--- a/web-locks/query.tentative.https.html
+++ b/web-locks/query.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: navigator.locks.query method</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: navigator.locks.query method
+// META: script=resources/helpers.js
+
 'use strict';
 
 // Returns an array of the modes for the locks with matching name.
@@ -230,5 +225,3 @@
   assert_equals(res1_held_clients[0], res2_pending_clients[0]);
   assert_equals(res2_held_clients[0], res1_pending_clients[0]);
 }, 'query() can observe a deadlock');
-
-</script>
diff --git a/web-locks/resource-names.tentative.https.html b/web-locks/resource-names.tentative.https.any.js
similarity index 84%
rename from web-locks/resource-names.tentative.https.html
rename to web-locks/resource-names.tentative.https.any.js
index 1dfe11d..dbcd986 100644
--- a/web-locks/resource-names.tentative.https.html
+++ b/web-locks/resource-names.tentative.https.any.js
@@ -1,10 +1,5 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: Resources DOMString edge cases</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
+// META: title=Web Locks API: Resources DOMString edge cases
+
 'use strict';
 
 function code_points(s) {
@@ -58,5 +53,3 @@
   });
   assert_true(got_lock, 'Names with embedded "-" should be accepted');
 }, 'Names cannot start with "-"');
-
-</script>
diff --git a/web-locks/secure-context.tentative.https.any.js b/web-locks/secure-context.tentative.https.any.js
new file mode 100644
index 0000000..2e1d391
--- /dev/null
+++ b/web-locks/secure-context.tentative.https.any.js
@@ -0,0 +1,13 @@
+// META: title=Web Locks API: API requires secure context
+
+'use strict';
+
+test(t => {
+  assert_true(self.isSecureContext);
+  assert_idl_attribute(navigator, 'locks',
+                       'navigator.locks exists in secure context');
+  assert_true('LockManager' in self,
+              'LockManager is present in secure contexts');
+  assert_true('Lock' in self,
+              'Lock interface is present in secure contexts');
+}, 'API presence in secure contexts');
diff --git a/web-locks/secure-context.tentative.https.html b/web-locks/secure-context.tentative.https.html
deleted file mode 100644
index b5b6fea..0000000
--- a/web-locks/secure-context.tentative.https.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: API requires secure context</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-'use strict';
-
-test(t => {
-  assert_true(window.isSecureContext);
-  assert_idl_attribute(navigator, 'locks',
-                       'navigator.locks exists in secure context');
-  assert_true('LockManager' in self,
-              'LockManager is present in secure contexts');
-  assert_true('Lock' in self,
-              'Lock interface is present in secure contexts');
-}, 'API presence in secure contexts');
-</script>
diff --git a/web-locks/signal.tentative.https.html b/web-locks/signal.tentative.https.any.js
similarity index 94%
rename from web-locks/signal.tentative.https.html
rename to web-locks/signal.tentative.https.any.js
index 94f07a5..424b219 100644
--- a/web-locks/signal.tentative.https.html
+++ b/web-locks/signal.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: AbortSignal integration</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: AbortSignal integration
+// META: script=resources/helpers.js
+
 'use strict';
 
 function makePromiseAndResolveFunc() {
@@ -198,5 +193,3 @@
                 'Lock released promise should not reject');
 
 }, 'Abort signaled after lock released');
-
-</script>
diff --git a/web-locks/steal.tentative.https.html b/web-locks/steal.tentative.https.any.js
similarity index 87%
rename from web-locks/steal.tentative.https.html
rename to web-locks/steal.tentative.https.any.js
index b796c24..ca99137 100644
--- a/web-locks/steal.tentative.https.html
+++ b/web-locks/steal.tentative.https.any.js
@@ -1,11 +1,6 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web Locks API: steal option</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/helpers.js"></script>
-<script>
+// META: title=Web Locks API: steal option
+// META: script=resources/helpers.js
+
 'use strict';
 
 const never_settled = new Promise(resolve => { /* never */ });
@@ -93,5 +88,3 @@
   assert_true(saw_abort, 'First steal should have aborted');
 
 }, 'Last caller wins');
-
-</script>
diff --git a/web-locks/workers.tentative.https.html b/web-locks/workers.tentative.https.html
index 1e69919..04af72c 100644
--- a/web-locks/workers.tentative.https.html
+++ b/web-locks/workers.tentative.https.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web Locks API: Workers</title>
-<link rel=help href="https://github.com/inexorabletash/web-locks">
+<link rel=help href="https://wicg.github.io/web-locks/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/helpers.js"></script>
diff --git a/web-nfc/idlharness.https.window.js b/web-nfc/idlharness.https.window.js
index 1d6457b..0b3b099 100644
--- a/web-nfc/idlharness.https.window.js
+++ b/web-nfc/idlharness.https.window.js
@@ -13,5 +13,5 @@
       Navigator: ['navigator'],
       NFC: ['navigator.nfc'],
     });
-  },
-  'Test IDL implementation of Web NFC API');
+  }
+);
diff --git a/web-share/idlharness.https.window.js b/web-share/idlharness.https.window.js
index 6a69c8d..cbaf9d7 100644
--- a/web-share/idlharness.https.window.js
+++ b/web-share/idlharness.https.window.js
@@ -12,6 +12,5 @@
     idl_array.add_objects({
       Navigator: ['navigator']
     });
-  },
-  'Test driver'
+  }
 );
diff --git a/webaudio/idlharness.https.window.js b/webaudio/idlharness.https.window.js
index eb4734b..c2bae34 100644
--- a/webaudio/idlharness.https.window.js
+++ b/webaudio/idlharness.https.window.js
@@ -70,6 +70,5 @@
     await context.audioWorklet.addModule(
       'the-audio-api/the-audioworklet-interface/processors/dummy-processor.js');
     self.worklet_node = new AudioWorkletNode(context, 'dummy');
-  },
-  'Test driver'
+  }
 );
diff --git a/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html b/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html
new file mode 100644
index 0000000..dd964ef
--- /dev/null
+++ b/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>Test that DelayNode output channelCount matches that of the delayed input</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// See https://github.com/WebAudio/web-audio-api/issues/25
+
+// sampleRate is a power of two so that delay times are exact in base-2
+// floating point arithmetic.
+const SAMPLE_RATE = 32768;
+// Arbitrary delay time in frames (but this is assumed a multiple of block
+// size below):
+const DELAY_FRAMES = 3 * 128;
+// Implementations may apply interpolation to input samples, which can spread
+// the effect of input with larger channel counts over neighbouring blocks.
+// This test ignores enough neighbouring blocks to ignore the effects of
+// filter radius of up to this number of frames:
+const INTERPOLATION_GRACE = 128;
+// Number of frames of DelayNode output that are known to be stereo:
+const STEREO_FRAMES = 128;
+// The delay will be increased at this frame to switch DelayNode output back
+// to mono.
+const MONO_OUTPUT_START_FRAME =
+  DELAY_FRAMES + INTERPOLATION_GRACE + STEREO_FRAMES;
+// Number of frames of output that are known to be mono after the known stereo
+// and interpolation grace.
+const MONO_FRAMES = 128;
+// Total length allows for interpolation after effects of stereo input are
+// finished and one block to test return to mono output:
+const TOTAL_LENGTH =
+  MONO_OUTPUT_START_FRAME + INTERPOLATION_GRACE + MONO_FRAMES;
+// maxDelayTime, is a multiple of block size, because the Gecko implementation
+// once had a bug with delayTime = maxDelayTime in this situation:
+const MAX_DELAY_FRAMES = TOTAL_LENGTH + INTERPOLATION_GRACE;
+
+promise_test(() => {
+  let context = new OfflineAudioContext({numberOfChannels: 1,
+                                         length: TOTAL_LENGTH,
+                                         sampleRate: SAMPLE_RATE});
+
+  // Only channel 1 of the splitter is connected to the destination.
+  let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+  splitter.connect(context.destination, 1);
+
+  // A gain node has channelCountMode "max" and channelInterpretation
+  // "speakers", and so will up-mix a mono input when there is stereo input.
+  let gain = new GainNode(context);
+  gain.connect(splitter);
+
+  // The delay node initially outputs a single channel of silence, when it
+  // does not have enough signal in its history to output what it has
+  // previously received.  After the delay period, it will then output the
+  // stereo signal it received.
+  let delay =
+      new DelayNode(context,
+                    {maxDelayTime: MAX_DELAY_FRAMES / context.sampleRate,
+                     delayTime: DELAY_FRAMES / context.sampleRate});
+  // Schedule an increase in the delay to return to mono silent output from
+  // the unfilled portion of the DelayNode's buffer.
+  delay.delayTime.setValueAtTime(MAX_DELAY_FRAMES / context.sampleRate,
+                                 MONO_OUTPUT_START_FRAME / context.sampleRate);
+  delay.connect(gain);
+
+  let stereoMerger = new ChannelMergerNode(context, {numberOfInputs: 2});
+  stereoMerger.connect(delay);
+
+  let leftOffset = 0.125;
+  let rightOffset = 0.5;
+  let leftSource = new ConstantSourceNode(context, {offset: leftOffset});
+  let rightSource = new ConstantSourceNode(context, {offset: rightOffset});
+  leftSource.start();
+  rightSource.start();
+  leftSource.connect(stereoMerger, 0, 0);
+  rightSource.connect(stereoMerger, 0, 1);
+  // Connect a mono source directly to the gain, so that even stereo silence
+  // will be detected in channel 1 of the gain output because it will cause
+  // the mono source to be up-mixed.
+  let monoOffset = 0.25
+  let monoSource = new ConstantSourceNode(context, {offset: monoOffset});
+  monoSource.start();
+  monoSource.connect(gain);
+
+  return context.startRendering().
+    then((buffer) => {
+      let output = buffer.getChannelData(0);
+
+      function assert_samples_equal(startIndex, length, expected, description)
+      {
+        for (let i = startIndex; i < startIndex + length; ++i) {
+          assert_equals(output[i], expected, description + ` at ${i}`);
+        }
+      }
+
+      assert_samples_equal(0, DELAY_FRAMES - INTERPOLATION_GRACE,
+                           0, "Initial mono");
+      assert_samples_equal(DELAY_FRAMES + INTERPOLATION_GRACE, STEREO_FRAMES,
+                           monoOffset + rightOffset, "Stereo");
+      assert_samples_equal(MONO_OUTPUT_START_FRAME + INTERPOLATION_GRACE,
+                           MONO_FRAMES,
+                           0, "Final mono");
+    });
+});
+
+</script>
diff --git a/webauthn/idlharness.https.window.js b/webauthn/idlharness.https.window.js
index 8ca5b99..060b0c1 100644
--- a/webauthn/idlharness.https.window.js
+++ b/webauthn/idlharness.https.window.js
@@ -17,8 +17,7 @@
         PublicKeyCredential: ['cred'],
         AuthenticatorAssertionResponse: ['assertionResponse']
       });
-    },
-    'WebAuthn interfaces.'
+    }
   );
 
   let challengeBytes = new Uint8Array(16);
diff --git a/webdriver/tests/support/fixtures.py b/webdriver/tests/support/fixtures.py
index b4a2996..daeccb7 100644
--- a/webdriver/tests/support/fixtures.py
+++ b/webdriver/tests/support/fixtures.py
@@ -263,12 +263,19 @@
         assert isinstance(text, basestring), "`text` parameter must be a string"
 
         # Script completes itself when the user prompt has been opened.
+        # For prompt() dialogs, add a value for the 'default' argument,
+        # as some user agents (IE, for example) do not produce consistent
+        # values for the default.
         session.execute_async_script("""
             let dialog_type = arguments[0];
             let text = arguments[1];
 
             setTimeout(function() {
-              window.dialog_return_value = window[dialog_type](text);
+              if (dialog_type == 'prompt') {
+                window.dialog_return_value = window[dialog_type](text, '');
+              } else {
+                window.dialog_return_value = window[dialog_type](text);
+              }
             }, 0);
             """, args=(dialog_type, text))
 
diff --git a/webgl/webgl1-idlharness.window.js b/webgl/webgl1-idlharness.window.js
index 263fd81..884def8 100644
--- a/webgl/webgl1-idlharness.window.js
+++ b/webgl/webgl1-idlharness.window.js
@@ -10,6 +10,5 @@
   ['dom'],
   idl_array => {
     // TODO: objects
-  },
-  'webgl1 interfaces'
+  }
 );
diff --git a/webgl/webgl2-idlharness.window.js b/webgl/webgl2-idlharness.window.js
index ed2c2f6..97edfa1 100644
--- a/webgl/webgl2-idlharness.window.js
+++ b/webgl/webgl2-idlharness.window.js
@@ -10,6 +10,5 @@
   ['webgl1', 'dom'],
   idl_array => {
     // TODO: objects
-  },
-  'webgl2 interfaces'
+  }
 );
diff --git a/webmessaging/message-channels/dictionary-transferrable.html b/webmessaging/message-channels/dictionary-transferrable.html
new file mode 100644
index 0000000..f7ab108
--- /dev/null
+++ b/webmessaging/message-channels/dictionary-transferrable.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>basic messagechannel with transfer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+  var channel = new MessageChannel();
+  var ab = new ArrayBuffer(1);
+  channel.port1.postMessage(ab, {transfer: [ab]});
+  channel.port2.onmessage = t.step_func(
+    function(e) {
+      assert_equals(e.data.byteLength, 1);
+      t.done();
+    });
+  channel.port2.start();
+});
+</script>
diff --git a/webrtc-stats/idlharness.window.js b/webrtc-stats/idlharness.window.js
index ead85cb..2374120 100644
--- a/webrtc-stats/idlharness.window.js
+++ b/webrtc-stats/idlharness.window.js
@@ -10,5 +10,5 @@
   [], // No deps
   idl_array => {
     // No interfaces to test
-  },
-  'webrtc-stats interfaces.');
+  }
+);
diff --git a/webstorage/idlharness.window.js b/webstorage/idlharness.window.js
index 7a6f16a..177eb60 100644
--- a/webstorage/idlharness.window.js
+++ b/webstorage/idlharness.window.js
@@ -25,6 +25,5 @@
       ],
       StorageEvent: ['new StorageEvent("storage")']
     });
-  },
-  'webstorage interfaces'
+  }
 );
diff --git a/webusb/idlharness.https.any.js b/webusb/idlharness.https.any.js
index 9ba0198..0260be2 100644
--- a/webusb/idlharness.https.any.js
+++ b/webusb/idlharness.https.any.js
@@ -41,6 +41,5 @@
       self.usbConnectionEvent =
           new USBConnectionEvent('connect', { device: usbDevice });
     }, 'USB device setup');
-  },
-  'WebUSB IDL test'
+  }
 );
diff --git a/webvtt/api/idlharness.window.js b/webvtt/api/idlharness.window.js
new file mode 100644
index 0000000..53c1ded
--- /dev/null
+++ b/webvtt/api/idlharness.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+idl_test(
+  ['webvtt'],
+  ['html', 'dom'],
+  idl_array => {
+    idl_array.add_objects({
+      VTTCue: ['new VTTCue(0, 0, "")'],
+      VTTRegion: ['new VTTRegion()'],
+    });
+  }
+);
diff --git a/webvtt/api/interfaces.html b/webvtt/api/interfaces.html
deleted file mode 100644
index 5222256..0000000
--- a/webvtt/api/interfaces.html
+++ /dev/null
@@ -1,138 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>WebVTT IDL tests</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=/resources/WebIDLParser.js></script>
-<script src=/resources/idlharness.js></script>
-
-<h1>WebVTT IDL tests</h1>
-<div id=log></div>
-
-<script type=text/plain id=untested>
-// HTML
-interface TextTrackCue : EventTarget {
-  readonly attribute TextTrack? track;
-
-  attribute DOMString id;
-  attribute double startTime;
-  attribute double endTime;
-  attribute boolean pauseOnExit;
-
-  attribute EventHandler onenter;
-  attribute EventHandler onexit;
-};
-
-[TreatNonObjectAsNull]
-callback EventHandlerNonNull = any (Event event);
-typedef EventHandlerNonNull? EventHandler;
-
-// DOM
-[Exposed=(Window,Worker)]
-interface EventTarget {
-  void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);
-  void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
-  boolean dispatchEvent(Event event);
-};
-
-callback interface EventListener {
-  void handleEvent(Event event);
-};
-
-dictionary EventListenerOptions {
-  boolean capture = false;
-};
-
-dictionary AddEventListenerOptions : EventListenerOptions {
-  boolean passive = false;
-  boolean once = false;
-};
-
-[Constructor,
- Exposed=Window]
-interface DocumentFragment : Node {
-};
-
-[Exposed=Window]
-interface Node : EventTarget {
-  const unsigned short ELEMENT_NODE = 1;
-  const unsigned short ATTRIBUTE_NODE = 2;
-  const unsigned short TEXT_NODE = 3;
-  const unsigned short CDATA_SECTION_NODE = 4;
-  const unsigned short ENTITY_REFERENCE_NODE = 5; // historical
-  const unsigned short ENTITY_NODE = 6; // historical
-  const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
-  const unsigned short COMMENT_NODE = 8;
-  const unsigned short DOCUMENT_NODE = 9;
-  const unsigned short DOCUMENT_TYPE_NODE = 10;
-  const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
-  const unsigned short NOTATION_NODE = 12; // historical
-  readonly attribute unsigned short nodeType;
-  readonly attribute DOMString nodeName;
-
-  readonly attribute USVString baseURI;
-
-  readonly attribute boolean isConnected;
-  readonly attribute Document? ownerDocument;
-  Node getRootNode(optional GetRootNodeOptions options);
-  readonly attribute Node? parentNode;
-  readonly attribute Element? parentElement;
-  boolean hasChildNodes();
-  [SameObject] readonly attribute NodeList childNodes;
-  readonly attribute Node? firstChild;
-  readonly attribute Node? lastChild;
-  readonly attribute Node? previousSibling;
-  readonly attribute Node? nextSibling;
-
-  [CEReactions] attribute DOMString? nodeValue;
-  [CEReactions] attribute DOMString? textContent;
-  [CEReactions] void normalize();
-
-  [CEReactions, NewObject] Node cloneNode(optional boolean deep = false);
-  boolean isEqualNode(Node? otherNode);
-  boolean isSameNode(Node? otherNode); // historical alias of ===
-
-  const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
-  const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02;
-  const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04;
-  const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08;
-  const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
-  const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
-  unsigned short compareDocumentPosition(Node other);
-  boolean contains(Node? other);
-
-  DOMString? lookupPrefix(DOMString? namespace);
-  DOMString? lookupNamespaceURI(DOMString? prefix);
-  boolean isDefaultNamespace(DOMString? namespace);
-
-  [CEReactions] Node insertBefore(Node node, Node? child);
-  [CEReactions] Node appendChild(Node node);
-  [CEReactions] Node replaceChild(Node node, Node child);
-  [CEReactions] Node removeChild(Node child);
-};
-
-dictionary GetRootNodeOptions {
-  boolean composed = false;
-};
-</script>
-
-<script>
-"use strict";
-
-// https://w3c.github.io/webvtt/
-
-promise_test(async () => {
-  const webvttIDL = await fetch('/interfaces/webvtt.idl').then(response =>
-    response.text(),
-  );
-  var idlArray = new IdlArray();
-  idlArray.add_untested_idls(document.getElementById('untested').textContent);
-  idlArray.add_idls(webvttIDL);
-  idlArray.add_objects({
-    VTTCue: ['new VTTCue(0, 0, "")'],
-    VTTRegion: ['new VTTRegion()'],
-  });
-  idlArray.test();
-  done();
-}, 'webvtt interfaces.');
-</script>
diff --git a/webxr/idlharness.https.window.js b/webxr/idlharness.https.window.js
index 6ec963d..9576379 100644
--- a/webxr/idlharness.https.window.js
+++ b/webxr/idlharness.https.window.js
@@ -17,6 +17,5 @@
     });
     self.device = await navigator.XR.requestDevice();
     self.session = await device.requestSession();
-  },
-  'Test IDL implementation of WebXR API'
+  }
 );
diff --git a/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-dictionary.html b/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-dictionary.html
new file mode 100644
index 0000000..e086dc1
--- /dev/null
+++ b/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-dictionary.html
@@ -0,0 +1,29 @@
+<!--
+onmessage = (event) => {
+  try {
+    postMessage(event.data, {transfer: [event.data]});
+  } catch(e) {
+    postMessage(''+e);
+  }
+}
+/*
+-->
+<!doctype html>
+<title>Using dictionary as postMessage's second argument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+(async_test()).step(function() {
+  var worker = new Worker('#');
+  var ab = new ArrayBuffer(1);
+  worker.postMessage(ab, {transfer: [ab]});
+  worker.onmessage = this.step_func(function(e) {
+    assert_equals(e.data.byteLength, 1);
+    this.done();
+  });
+});
+</script>
+<!--
+*/
+//-->
diff --git a/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null.html b/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null.html
index e81a56b..4a03c83 100644
--- a/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null.html
+++ b/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null.html
@@ -15,7 +15,7 @@
 (async_test()).step(function() {
   var worker = new Worker('#');
   worker.onmessage = this.step_func(function(e) {
-    assert_true(e.data);
+    assert_equals(1, e.data);
     this.done();
   });
 });
diff --git a/xhr/overridemimetype-edge-cases.window.js b/xhr/overridemimetype-edge-cases.window.js
index 6dfe755..192a696 100644
--- a/xhr/overridemimetype-edge-cases.window.js
+++ b/xhr/overridemimetype-edge-cases.window.js
@@ -5,7 +5,7 @@
   let secondTime = false;
   client.onload = t.step_func(() => {
     if(!secondTime) {
-      assert_equals(client.responseText, "\uFFFD");
+      assert_equals(client.responseText, "\uFFFD\uFFFD");
       secondTime = true;
       client.open("GET", testURL);
       client.send();
@@ -32,7 +32,7 @@
 async_test(t => {
   const client = new XMLHttpRequest();
   client.onload = t.step_func_done(() => {
-    assert_equals(client.responseText, "\uFFFD")
+    assert_equals(client.responseText, "\uFFFD\uFFFD")
   });
   client.open("GET", testURL);
   client.overrideMimeType("text/plain;charset=342");