Merge pull request #9397 from faceless2/bfo-css-fonts-3-fontfeature

Added new testcases to test font-variant precedence rules
diff --git a/2dcontext/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html b/2dcontext/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
index 5f1c7fa..1621c03 100644
--- a/2dcontext/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
+++ b/2dcontext/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
@@ -8,34 +8,29 @@
 <body class="show_output">
 
 <h1>2d.drawImage.zerocanvas</h1>
-<p class="desc"></p>
+<p class="desc">drawImage with zero-sized canvas as the source shoud throw exception</p>
 
 
 <p class="output">Actual output:</p>
 <canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
-<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+
 <ul id="d"></ul>
 <script>
-var t = async_test("");
+var t = async_test("drawImage with zero-sized canvas as the source shoud throw exception");
 _addTest(function(canvas, ctx) {
 
-ctx.fillStyle = '#0f0';
-ctx.fillRect(0, 0, 100, 50);
-
 var canvas2 = document.createElement('canvas');
 canvas2.width = 0;
-canvas2.height = 10;
-ctx.drawImage(canvas2, 0, 0);
+canvas2.height = 50;
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
 
-canvas2.width = 10;
+canvas2.width = 50;
 canvas2.height = 0;
-ctx.drawImage(canvas2, 0, 0);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
 
 canvas2.width = 0;
 canvas2.height = 0;
-ctx.drawImage(canvas2, 0, 0);
-
-_assertPixelApprox(canvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(canvas2, 0, 0); });
 
 
 });
diff --git a/2dcontext/tools/spec.yaml b/2dcontext/tools/spec.yaml
index 278dc95..e57bbc8 100644
--- a/2dcontext/tools/spec.yaml
+++ b/2dcontext/tools/spec.yaml
@@ -586,7 +586,7 @@
     text: "If the image argument is <...> an HTMLVideoElement object whose readyState attribute is either HAVE_NOTHING or HAVE_METADATA<^>, then the implementation *must* return without drawing anything."
   - id: 2d.drawImage.zerocanvas
     previously: [ 10, "dw and dh" ]
-    text: "If the image argument is an HTMLCanvasElement object with either a horizontal dimension or a vertical dimension equal to zero, then the implementation *must* return without drawing anything<^>."
+    text: "If the image argument is an HTMLCanvasElement or an OffscreenCanvas object with either a horizontal dimension or a vertical dimension equal to zero, then the implementation *must* throw an INVALID_STATE_ERR exception<^>."
   - id: 2d.drawImage.zerosource
     text: "If one of the sw or sh arguments is zero<^>, the implementation *must* return without drawing anything."
   - id: 2d.drawImage.paint
diff --git a/2dcontext/tools/tests2d.yaml b/2dcontext/tools/tests2d.yaml
index 7da08f9..f7b858a 100644
--- a/2dcontext/tools/tests2d.yaml
+++ b/2dcontext/tools/tests2d.yaml
@@ -9002,27 +9002,22 @@
   expected: green
 
 - name: 2d.drawImage.zerocanvas
+  desc: drawImage with zero-sized canvas as the source shoud throw exception
   testing:
     - 2d.drawImage.zerocanvas
   code: |
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-
     var canvas2 = document.createElement('canvas');
     canvas2.width = 0;
-    canvas2.height = 10;
-    ctx.drawImage(canvas2, 0, 0);
+    canvas2.height = 50;
+    @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
 
-    canvas2.width = 10;
+    canvas2.width = 50;
     canvas2.height = 0;
-    ctx.drawImage(canvas2, 0, 0);
+    @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
 
     canvas2.width = 0;
     canvas2.height = 0;
-    ctx.drawImage(canvas2, 0, 0);
-
-    @assert pixel 50,25 ==~ 0,255,0,255;
-  expected: green
+    @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
 
 - name: 2d.drawImage.svg
   desc: drawImage() of an SVG image
diff --git a/IndexedDB/interleaved-cursors.html b/IndexedDB/interleaved-cursors-common.js
similarity index 93%
rename from IndexedDB/interleaved-cursors.html
rename to IndexedDB/interleaved-cursors-common.js
index 3112690..a76ec52 100644
--- a/IndexedDB/interleaved-cursors.html
+++ b/IndexedDB/interleaved-cursors-common.js
@@ -1,12 +1,5 @@
-<!doctype html>
-<meta charset="utf-8">
-<meta name="timeout" content="long">
-<title>IndexedDB: Interleaved iteration of multiple cursors</title>
-<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support-promises.js"></script>
-<script>
+// Infrastructure shared by interleaved-cursors-{small,large}.html
+
 // Number of objects that each iterator goes over.
 const itemCount = 10;
 
@@ -169,7 +162,7 @@
   });
 }
 
-for (let cursorCount of [1, 10, 100, 500]) {
+function cursorTest(cursorCount) {
   promise_test(testCase => {
     return createDatabase(testCase, (database, transaction) => {
       const store = database.createObjectStore('cache',
@@ -193,4 +186,3 @@
     });
   }, `${cursorCount} cursors`);
 }
-</script>
diff --git a/IndexedDB/interleaved-cursors-large.html b/IndexedDB/interleaved-cursors-large.html
new file mode 100644
index 0000000..6f4e440
--- /dev/null
+++ b/IndexedDB/interleaved-cursors-large.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>IndexedDB: Interleaved iteration of multiple cursors</title>
+<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support-promises.js"></script>
+<script src="interleaved-cursors-common.js"></script>
+<script>
+cursorTest(250);
+</script>
diff --git a/IndexedDB/interleaved-cursors-small.html b/IndexedDB/interleaved-cursors-small.html
new file mode 100644
index 0000000..a4c4777
--- /dev/null
+++ b/IndexedDB/interleaved-cursors-small.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>IndexedDB: Interleaved iteration of multiple cursors</title>
+<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support-promises.js"></script>
+<script src="interleaved-cursors-common.js"></script>
+<script>
+cursorTest(1);
+cursorTest(10);
+cursorTest(100);
+</script>
diff --git a/README.md b/README.md
index 0247e8c..90f1f47 100644
--- a/README.md
+++ b/README.md
@@ -32,17 +32,22 @@
 and read the [Windows Notes](#windows-notes) section below.
 
 To get the tests running, you need to set up the test domains in your
-[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system). The
-following entries are required:
+[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system).
 
+The necessary content can be generated with `./wpt make-hosts-file`; on
+Windows, you will need to preceed the prior command with `python` or
+the path to the Python binary (`python wpt make-hosts-file`).
+
+For example, on most UNIX-like systems, you can setup the hosts file with:
+
+```bash
+./wpt make-hosts-file | sudo tee -a /etc/hosts
 ```
-127.0.0.1   web-platform.test
-127.0.0.1   www.web-platform.test
-127.0.0.1   www1.web-platform.test
-127.0.0.1   www2.web-platform.test
-127.0.0.1   xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1   xn--lve-6lad.web-platform.test
-0.0.0.0     nonexistent-origin.web-platform.test
+
+And on Windows (note this requires an Administrator privileged shell):
+
+```bash
+python wpt make-hosts-file >> %SystemRoot%\System32\drivers\etc\hosts
 ```
 
 If you are behind a proxy, you also need to make sure the domains above are
diff --git a/common/css-paint-tests.js b/common/worklet-reftest.js
similarity index 71%
rename from common/css-paint-tests.js
rename to common/worklet-reftest.js
index cd57332..e060835 100644
--- a/common/css-paint-tests.js
+++ b/common/worklet-reftest.js
@@ -1,12 +1,12 @@
 // To make sure that we take the snapshot at the right time, we do double
 // requestAnimationFrame. In the second frame, we take a screenshot, that makes
 // sure that we already have a full frame.
-function importPaintWorkletAndTerminateTestAfterAsyncPaint(code) {
-    if (typeof CSS.paintWorklet == "undefined") {
+function importWorkletAndTerminateTestAfterAsyncPaint(worklet, code) {
+    if (typeof worklet == "undefined") {
         takeScreenshot();
     } else {
         var blob = new Blob([code], {type: 'text/javascript'});
-        CSS.paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
+        worklet.addModule(URL.createObjectURL(blob)).then(function() {
             requestAnimationFrame(function() {
                 requestAnimationFrame(function() {
                     takeScreenshot();
diff --git a/compat/webkit-appearance.tentative.html b/compat/webkit-appearance.tentative.html
new file mode 100644
index 0000000..8c0273d
--- /dev/null
+++ b/compat/webkit-appearance.tentative.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<title>-webkit-appearance support</title>
+<!-- There is no spec for -webkit-appearance. It is supported in Opera, Safari,
+     Chrome, and Edge. Firefox has expressed intent to support it. -->
+<link rel="help" href="https://github.com/whatwg/compat/issues/6">
+<meta name="assert" content="This test checks for support of the -webkit-appearance CSS attribute." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="tester"></div>
+
+<script>
+const WEBKIT_APPEARANCE_VALUES = [
+  'none',
+  'checkbox',
+  'radio',
+  'push-button',
+  'square-button',
+  'button',
+  'button-bevel',
+  'inner-spin-button',
+  'listbox',
+  'listitem',
+  'media-enter-fullscreen-button',
+  'media-exit-fullscreen-button',
+  'media-mute-button',
+  'media-play-button',
+  'media-overlay-play-button',
+  'media-toggle-closed-captions-button',
+  'media-slider',
+  'media-sliderthumb',
+  'media-volume-slider-container',
+  'media-volume-slider',
+  'media-volume-sliderthumb',
+  'media-controls-background',
+  'media-controls-fullscreen-background',
+  'media-current-time-display',
+  'media-time-remaining-display',
+  'menulist',
+  'menulist-button',
+  'menulist-text',
+  'menulist-textfield',
+  'meter',
+  'progress-bar',
+  'progress-bar-value',
+  'slider-horizontal',
+  'slider-vertical',
+  'sliderthumb-horizontal',
+  'sliderthumb-vertical',
+  'caret',
+  'searchfield',
+  'searchfield-cancel-button',
+  'textfield',
+  'textarea',
+];
+
+for (const appearance_value of WEBKIT_APPEARANCE_VALUES) {
+  test(() => {
+    const div = document.getElementById('tester');
+    div.style = `-webkit-appearance: ${appearance_value}`;
+    assert_equals(getComputedStyle(div).webkitAppearance, appearance_value);
+  });
+}
+</script>
diff --git a/console/console-format-specifier-symbol-manual.html b/console/console-format-specifier-symbol-manual.html
new file mode 100644
index 0000000..47a981f
--- /dev/null
+++ b/console/console-format-specifier-symbol-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Console Format Specifiers on Symbols</title>
+<meta name="author" title="Dominic Farolino" href="mailto:domfarolino@gmail.com">
+<meta name="assert" content="Console format specifiers on Symbols">
+<link rel="help" href="https://console.spec.whatwg.org/#formatter">
+</head>
+<body>
+<p>Open the console inside the developer tools. It should contain five lines, each of which are:</p>
+<p><code>Symbol(description)</code></p>
+
+<script>
+console.log("%s", Symbol.for("description"));
+console.dirxml("%s", Symbol.for("description"));
+console.trace("%s", Symbol.for("description"));
+console.group("%s", Symbol.for("description"));
+console.groupCollapsed("%s", Symbol.for("description"));
+</script>
+</body>
+</html>
diff --git a/cookie-store/cookie-store.idl b/cookie-store/cookie-store.idl
new file mode 100644
index 0000000..d4f6459
--- /dev/null
+++ b/cookie-store/cookie-store.idl
@@ -0,0 +1,56 @@
+// https://github.com/WICG/cookie-store/blob/gh-pages/explainer.md
+
+dictionary CookieListItem {
+  USVString name;
+  USVString value;
+};
+
+typedef sequence<CookieListItem> CookieList;
+
+[
+  Exposed=(ServiceWorker,Window)
+] interface CookieStore {
+  Promise<CookieList?> getAll(USVString name, optional CookieStoreGetOptions options);
+  Promise<CookieList?> getAll(optional CookieStoreGetOptions options);
+
+  Promise<CookieListItem?> get(USVString name, optional CookieStoreGetOptions options);
+  Promise<CookieListItem?> get(optional CookieStoreGetOptions options);
+
+  Promise<boolean> has(USVString name, optional CookieStoreGetOptions options);
+  Promise<boolean> has(optional CookieStoreGetOptions options);
+
+  Promise<void> set(USVString name, USVString value, optional CookieStoreSetOptions options);
+  Promise<void> set(CookieStoreSetOptions options);
+
+  Promise<void> delete(USVString name, optional CookieStoreSetOptions options);
+  Promise<void> delete(CookieStoreSetOptions options);
+};
+
+enum CookieMatchType {
+  "equals",
+  "startsWith"
+};
+
+dictionary CookieStoreGetOptions {
+  USVString name;
+  USVString url;
+  CookieMatchType matchType = "equals";
+};
+
+dictionary CookieStoreSetOptions {
+  USVString name;
+  USVString value;
+  DOMTimeStamp? expires = null;
+  USVString domain;
+  USVString path = "/";
+  boolean? secure;
+  boolean httpOnly = false;
+};
+
+partial interface Window {
+  [Replaceable, SameObject] readonly attribute CookieStore cookieStore;
+};
+
+partial interface ServiceWorkerGlobalScope {
+  [Replaceable, SameObject] readonly attribute CookieStore cookieStore;
+};
diff --git a/cookie-store/idlharness.tentative.html b/cookie-store/idlharness.tentative.html
new file mode 100644
index 0000000..cf80246
--- /dev/null
+++ b/cookie-store/idlharness.tentative.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Cookies: IDL tests</title>
+<link rel="help" href="https://github.com/WICG/cookie-store"/>
+<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>
+'use strict';
+
+promise_test(async t => {
+  const urls = ['/interfaces/html.idl', 'cookie-store.idl'];
+  const [html, cookie_store] = await Promise.all(
+    urls.map(url => fetch(url).then(response => response.text())));
+
+  const idl_array = new IdlArray();
+
+  // Dependencies of HTML
+  idl_array.add_untested_idls('interface Document {};');
+  idl_array.add_untested_idls('interface LinkStyle {};');
+  idl_array.add_untested_idls(html);
+
+  idl_array.add_untested_idls(
+    `[Global=ServiceWorker, Exposed=ServiceWorker]
+     interface ServiceWorkerGlobalScope {};`);
+
+  idl_array.add_idls(cookie_store);
+
+  idl_array.add_objects({
+    CookieStore: [self.cookieStore],
+  });
+  idl_array.test();
+}, 'Interface test');
+</script>
diff --git a/cookie-store/idlharness_serviceworker.js b/cookie-store/idlharness_serviceworker.js
new file mode 100644
index 0000000..35eccfd
--- /dev/null
+++ b/cookie-store/idlharness_serviceworker.js
@@ -0,0 +1,31 @@
+self.GLOBAL = {
+  isWindow: function() { return false; },
+  isWorker: function() { return true; },
+};
+importScripts('/resources/testharness.js',
+              '/resources/WebIDLParser.js',
+              '/resources/idlharness.js');
+
+promise_test(async t => {
+  const urls = ['cookie-store.idl'];
+  const [cookie_store] = await Promise.all(
+    urls.map(url => fetch(url).then(response => response.text())));
+
+  const idl_array = new IdlArray();
+
+  idl_array.add_untested_idls(
+    `[Global=ServiceWorker, Exposed=ServiceWorker]
+     interface ServiceWorkerGlobalScope {};`);
+  idl_array.add_untested_idls(
+    `[Global=Window, Exposed=Window]
+     interface Window {};`);
+
+  idl_array.add_idls(cookie_store);
+
+  idl_array.add_objects({
+    CookieStore: [self.cookieStore],
+  });
+  idl_array.test();
+}, 'Interface test');
+
+done();
diff --git a/cookie-store/idlharness_serviceworker.tentative.https.html b/cookie-store/idlharness_serviceworker.tentative.https.html
new file mode 100644
index 0000000..845a9fd
--- /dev/null
+++ b/cookie-store/idlharness_serviceworker.tentative.https.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Cookies: ServiceWorker IDL tests</title>
+<link rel="help" href="https://github.com/WICG/cookie-store">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+(async () => {
+  const scope = 'does/not/exist';
+
+  let registration = await navigator.serviceWorker.getRegistration(scope);
+  if (registration)
+    await registration.unregister();
+  registration = await navigator.serviceWorker.register(
+      'idlharness_serviceworker.js', {scope});
+
+  fetch_tests_from_worker(registration.installing);
+})();
+</script>
diff --git a/css/CSS2/floats/intrinsic-size-float-and-line.html b/css/CSS2/floats/intrinsic-size-float-and-line.html
new file mode 100644
index 0000000..0603124
--- /dev/null
+++ b/css/CSS2/floats/intrinsic-size-float-and-line.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Put float next to line in shrink-to-fit container</title>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#float-width" title="10.3.5 Floating, non-replaced elements">
+<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="float:left; min-width:150px; background:red;">
+  <div style="float:right; width:100px; height:200px; background:green;"></div>
+  <div style="vertical-align:top; display:inline-block; width:100px; height:200px; background:green;"></div>
+</div>
diff --git a/css/CSS2/normal-flow/margin-collapse-through-zero-height-block.html b/css/CSS2/normal-flow/margin-collapse-through-zero-height-block.html
new file mode 100644
index 0000000..471a4c7
--- /dev/null
+++ b/css/CSS2/normal-flow/margin-collapse-through-zero-height-block.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Collapse bottom margin from previous sibling through zero height block to next sibling</title>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#collapsing-margins" title="8.3.1 Collapsing margins">
+<link rel="match" href="../../reference/ref-filled-green-200px-square.html">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="overflow:hidden; width:200px; height:400px; background:green;">
+  <div style="margin-bottom:200px;"></div>
+  <div style="height:0;"></div>
+  <div style="height:200px; margin-top:100px; background:white;"></div>
+  <div style="height:200px; background:red;"></div>
+</div>
diff --git a/css/CSS2/visudet/reference/content-height-005-ref.html b/css/CSS2/visudet/reference/content-height-005-ref.html
index 70a3ee0..b68f8a9 100644
--- a/css/CSS2/visudet/reference/content-height-005-ref.html
+++ b/css/CSS2/visudet/reference/content-height-005-ref.html
@@ -2,6 +2,7 @@
 <meta charset="utf-8">
 <title>CSS Test Reference file</title>
 <link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
+<style>
 
 div {
   font-size: 50px;
diff --git a/css/css-align/content-distribution/place-content-shorthand-004.html b/css/css-align/content-distribution/place-content-shorthand-004.html
index 30f45b6..3f9bd88 100644
--- a/css/css-align/content-distribution/place-content-shorthand-004.html
+++ b/css/css-align/content-distribution/place-content-shorthand-004.html
@@ -48,6 +48,12 @@
     }, "Verify 'auto' values are invalid");
 
     test(function() {
+        checkInvalidValues("self-start")
+        checkInvalidValues("center self-end")
+        checkInvalidValues("self-end start")
+    }, "Verify self-position values are invalid");
+
+    test(function() {
         checkInvalidValues("")
     }, "Verify empty declaration is invalid");
 </script>
diff --git a/css/css-align/default-alignment/parse-justify-items-002.html b/css/css-align/default-alignment/parse-justify-items-002.html
index 6ba2c7c..1806fa2 100644
--- a/css/css-align/default-alignment/parse-justify-items-002.html
+++ b/css/css-align/default-alignment/parse-justify-items-002.html
@@ -19,39 +19,39 @@
 test(function() {
     element = document.createElement("div");
     document.body.appendChild(element);
-    checkValues(element, "justifyItems", "justify-items", "", "legacy");
+    checkValues(element, "justifyItems", "justify-items", "", "normal");
 }, "Test 'initial' value when nothing is specified");
 
 test(function() {
     container.style.display = "";
-    checkInitialValues(element, "justifyItems", "justify-items", "center", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "center", "normal");
 }, "Test justify-items: 'initial'");
 
 test(function() {
     container.style.display = "grid";
-    checkInitialValues(element, "justifyItems", "justify-items", "safe start", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "safe start", "normal");
 }, "Test grid items justify-items: 'initial'");
 
 test(function() {
     container.style.display = "flex";
-    checkInitialValues(element, "justifyItems", "justify-items", "unsafe end", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "unsafe end", "normal");
 }, "Test flex items justify-items: 'initial'");
 
 test(function() {
     container.style.display = "";
     element.style.position = "absolute";
-    checkInitialValues(element, "justifyItems", "justify-items", "start", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "start", "normal");
 }, "Test absolute positioned elements justify-items: 'initial'");
 
 test(function() {
     container.style.display = "grid";
     element.style.position = "absolute";
-    checkInitialValues(element, "justifyItems", "justify-items", "end", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "end", "normal");
 }, "Test absolute positioned grid items justify-items: 'initial'");
 
 test(function() {
     container.style.display = "flex";
     element.style.position = "absolute";
-    checkInitialValues(element, "justifyItems", "justify-items", "end", "legacy");
+    checkInitialValues(element, "justifyItems", "justify-items", "end", "normal");
 }, "Test absolute positioned flex items justify-items: 'initial'");
 </script>
diff --git a/css/css-align/default-alignment/place-items-shorthand-004.html b/css/css-align/default-alignment/place-items-shorthand-004.html
index c5d9472..aa3ff30 100644
--- a/css/css-align/default-alignment/place-items-shorthand-004.html
+++ b/css/css-align/default-alignment/place-items-shorthand-004.html
@@ -37,7 +37,18 @@
        checkInvalidValues("auto")
        checkInvalidValues("auto right")
        checkInvalidValues("auto auto")
-    }, "Verify 'auto' value is invalid as first longhand value.");
+       checkInvalidValues("center auto")
+    }, "Verify 'auto' value is invalid.");
+
+    test(function() {
+       checkInvalidValues("legacy")
+       checkInvalidValues("legacy start")
+       checkInvalidValues("end legacy")
+       checkInvalidValues("legacy left")
+       checkInvalidValues("center legacy")
+       checkInvalidValues("start legacy center")
+    }, "Verify 'legacy' value is invalid.");
+
 
     test(function() {
         checkInvalidValues("")
diff --git a/css/css-backgrounds/background-331.html b/css/css-backgrounds/background-331.html
index 3d72ad4..1216234 100644
--- a/css/css-backgrounds/background-331.html
+++ b/css/css-backgrounds/background-331.html
@@ -57,7 +57,7 @@
 
         test(function() {
             assert_equals(cs.getPropertyValue("background-color"),
-                "transparent", "background initial value for background-color");
+                "rgba(0, 0, 0, 0)", "background initial value for background-color");
         }, "background_initial_color");
     </script>
   </body>
diff --git a/css/css-backgrounds/background-332.html b/css/css-backgrounds/background-332.html
index da11dc8..2621cb6 100644
--- a/css/css-backgrounds/background-332.html
+++ b/css/css-backgrounds/background-332.html
@@ -57,7 +57,7 @@
 
         test(function() {
             assert_equals(cs.getPropertyValue("background-color"),
-                "gray", "background specified value for background-color");
+                "rgb(128, 128, 128)", "background specified value for background-color");
         }, "background_specified_color");
     </script>
   </body>
diff --git a/css/css-backgrounds/background-333.html b/css/css-backgrounds/background-333.html
index 8331527..53dd150 100644
--- a/css/css-backgrounds/background-333.html
+++ b/css/css-backgrounds/background-333.html
@@ -57,7 +57,7 @@
 
         test(function() {
             assert_equals(cs.getPropertyValue("background-color"),
-                "red", "background specified value for background-color");
+                "rgb(255, 0, 0)", "background specified value for background-color");
         }, "background_specified_color_color");
     </script>
   </body>
diff --git a/css/css-backgrounds/box-shadow-syntax-001.xht b/css/css-backgrounds/box-shadow-syntax-001.xht
index 4e32c9f..4df5907 100644
--- a/css/css-backgrounds/box-shadow-syntax-001.xht
+++ b/css/css-backgrounds/box-shadow-syntax-001.xht
@@ -21,7 +21,7 @@
     }
     .inset {
       color: green;
-      height: 13px;
+      height: 12px;
       margin: -1px 0 -4px -4px;
     }
 
diff --git a/css/css-backgrounds/reference/box-shadow-syntax-001.xht b/css/css-backgrounds/reference/box-shadow-syntax-001.xht
index 697c21a..34be7bc 100644
--- a/css/css-backgrounds/reference/box-shadow-syntax-001.xht
+++ b/css/css-backgrounds/reference/box-shadow-syntax-001.xht
@@ -10,6 +10,7 @@
       border: solid green 4px;
       padding-top: 1px;
       border-style: none none solid solid;
+      height: 7px;
     }
   </style>
  </head>
diff --git a/css/css-color/color-function-parsing.html b/css/css-color/color-function-parsing.html
new file mode 100644
index 0000000..7f483bb
--- /dev/null
+++ b/css/css-color/color-function-parsing.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Color 4: color() parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-color-4/#color-function">
+<meta name="assert" content="Tests basic parsing of the color function">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="test"></div>
+<script>
+    const div = document.querySelector("#test");
+    function testColorFunction(description, rule, expectedValue) {
+        test(function() {
+            div.style.color = "black";
+            div.style.color = rule;
+            assert_equals(getComputedStyle(div).color, expectedValue);
+        }, description);
+    }
+
+    testColorFunction("Basic sRGB white", "color(srgb 1 1 1)", "color(srgb 1 1 1)");
+    testColorFunction("White with lots of space", "color(    srgb         1      1 1       )", "color(srgb 1 1 1)");
+    testColorFunction("sRGB color", "color(srgb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+    testColorFunction("Different case for sRGB", "color(SrGb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+    testColorFunction("sRGB color with unnecessary decimals", "color(srgb 1.00000 0.500000 0.20)", "color(srgb 1 0.5 0.2)");
+    testColorFunction("sRGB white with 0.5 alpha", "color(srgb 1 1 1 / 0.5)", "color(srgb 1 1 1 / 0.5)");
+    testColorFunction("sRGB white with 0 alpha", "color(srgb 1 1 1 / 0)", "color(srgb 1 1 1 / 0)");
+    testColorFunction("sRGB white with 50% alpha", "color(srgb 1 1 1 / 50%)", "color(srgb 1 1 1 / 0.5)");
+    testColorFunction("sRGB white with 0% alpha", "color(srgb 1 1 1 / 0%)", "color(srgb 1 1 1 / 0)");
+    testColorFunction("One missing component is 0", "color(srgb 1 1)", "color(srgb 1 1 0)");
+    testColorFunction("Two missing components are 0", "color(srgb 1)", "color(srgb 1 0 0)");
+    testColorFunction("All components missing", "color(srgb)", "color(srgb 0 0 0)");
+    testColorFunction("Display P3 color", "color(display-p3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+    testColorFunction("Different case for Display P3", "color(dIspLaY-P3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+
+    testColorFunction("Unknown color space should fallback", "color(unknown 1 2 3, red)", "color(unknown 1 2 3, red)");
+
+    testColorFunction("sRGB color with negative component should clamp to 0", "color(srgb -0.25 0.5 0.75)", "color(srgb 0 0.5 0.75)");
+    testColorFunction("sRGB color with component > 1 should clamp", "color(srgb 0.25 1.5 0.75)", "color(srgb 0.25 1 0.75)");
+    testColorFunction("Display P3 color with negative component should clamp to 0", "color(display-p3 0.5 -199 0.75)", "color(display-p3 0.5 0 0.75)");
+    testColorFunction("Display P3 color with component > 1 should clamp", "color(display-p3 184 1.00001 2347329746587)", "color(display-p3 1 1 1)");
+    testColorFunction("Alpha > 1 should clamp", "color(srgb 0.1 0.2 0.3 / 1.9)", "color(srgb 0.1 0.2 0.3)");
+    testColorFunction("Negative alpha should clamp", "color(srgb 1 1 1 / -0.2)", "color(srgb 1 1 1 / 0)");
+
+    // Invalid properties
+    testColorFunction("Empty", "color()", "rgb(0, 0, 0)");
+    testColorFunction("Bad color space", "color(banana 1 1 1)", "rgb(0, 0, 0)");
+    testColorFunction("Bad Display P3 color space", "color(displayp3 1 1 1)", "rgb(0, 0, 0)");
+    testColorFunction("No color space", "color(1 1 1)", "rgb(0, 0, 0)");
+    testColorFunction("Too many parameters", "color(srgb 1 1 1 1)", "rgb(0, 0, 0)");
+    testColorFunction("Way too many parameters", "color(srgb 1 1 1 1 1)", "rgb(0, 0, 0)");
+    testColorFunction("Bad parameters", "color(srgb 1 eggs 1)", "rgb(0, 0, 0)");
+    testColorFunction("Bad alpha", "color(srgb 1 1 1 / bacon)", "rgb(0, 0, 0)");
+    testColorFunction("Junk after alpha", "color(srgb 1 1 1 / 1 cucumber)", "rgb(0, 0, 0)");
+</script>
diff --git a/css/css-fonts/font-variation-settings-serialization-001.html b/css/css-fonts/font-variation-settings-serialization-001.html
new file mode 100644
index 0000000..65178c9
--- /dev/null
+++ b/css/css-fonts/font-variation-settings-serialization-001.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>CSS Test: font-feature-settings serialization</title>
+        <link rel="author" title="Greg Whitworth" href="mailto:gwhit@microsoft.com">
+        <link rel="help" href="http://www.w3.org/TR/css-fonts-4/#propdef-font-variation-settings">
+        <link rel="issue" href="https://github.com/w3c/csswg-drafts/issues/1959#issuecomment-360864254">
+        <meta name="desctiption" content="It was decided in issue 1959 that font-variation-settings is a map, therefor duplicates should be removed">
+        <script src="/resources/testharness.js"></script>
+        <script src="/resources/testharnessreport.js"></script>
+    </head>
+    <body>
+        <div id="test1" style='font-variation-settings: "bldA" 1, "bldA" 2'></div>
+
+        <script>
+            const test1_canEqual = ['"bldA" 2', "'bldA' 2"];
+            test(function() {
+                assert_in_array(getComputedStyle(document.getElementById('test1')).fontVariationSettings, test1_canEqual);
+            }, "font-feature-settings should be serialized to not include duplicates");
+        </script>
+    </body>
+</html>
+
diff --git a/css/css-grid/grid-items/grid-minimum-size-grid-items-022.html b/css/css-grid/grid-items/grid-minimum-size-grid-items-022.html
index 66417a4..127ea8d 100644
--- a/css/css-grid/grid-items/grid-minimum-size-grid-items-022.html
+++ b/css/css-grid/grid-items/grid-minimum-size-grid-items-022.html
@@ -135,3 +135,217 @@
   <div data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
   <div data-expected-width="25"></div>
 </div>
+
+<h3>grid content writing-mode: vertical-lr;</h3>
+
+<pre>grid-template-columns: auto;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: auto;">
+  <div data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-columns: 0px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: 0px;">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: 25px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: 25px;">
+  <div data-expected-height="25">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px);</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px);</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item margin height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="15" style="margin: 5px 0px;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item padding height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25" style="padding: 5px 0px;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item border height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25" style="border: solid 5px blue;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<h3>grid content writing-mode: vertical-rl;</h3>
+
+<pre>grid-template-columns: auto;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: auto;">
+  <div data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-columns: 0px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: 0px;">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: 25px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: 25px;">
+  <div data-expected-height="25">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px);</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px);</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item margin height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="15" style="margin: 5px 0px;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item padding height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25" style="padding: 5px 0px;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item border height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="25" style="border: solid 5px blue;"></div>
+  <div data-expected-height="25"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 25px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 25px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="25"></div>
+</div>
diff --git a/css/css-grid/grid-items/grid-minimum-size-grid-items-023.html b/css/css-grid/grid-items/grid-minimum-size-grid-items-023.html
index d821e5c..636a40f 100644
--- a/css/css-grid/grid-items/grid-minimum-size-grid-items-023.html
+++ b/css/css-grid/grid-items/grid-minimum-size-grid-items-023.html
@@ -32,7 +32,7 @@
 
 <div id="log"></div>
 
-<h3>writing-mode: vertical-lr;</h3>
+<h3>item writing-mode: vertical-lr;</h3>
 
 <pre>grid-template-rows: auto;</pre>
 
@@ -139,7 +139,7 @@
   <div data-expected-height="25"></div>
 </div>
 
-<h3>writing-mode: vertical-rl;</h3>
+<h3>item writing-mode: vertical-rl;</h3>
 
 <pre>grid-template-rows: auto;</pre>
 
@@ -246,3 +246,216 @@
   <div data-expected-height="25"></div>
 </div>
 
+<h3>grid container writing-mode: vertical-lr; &amp; item writing-mode: horizontal-tb;</h3>
+
+<pre>grid-template-rows: auto;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: auto;">
+  <div class="horizontalTB" data-expected-width="100">XXXXXXXXXX</div>
+  <div data-expected-width="100"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px);</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: 25px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: 25px;">
+  <div class="horizontalTB" data-expected-width="25">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px);</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px);</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item margin width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0" style="margin: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item margin width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="15" style="margin: 0px 5px;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item padding width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="padding: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item padding width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25" style="padding: 0px 5px;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item border width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="border: solid 5px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item border width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25" style="border: solid 5px blue;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<h3>grid container writing-mode: vertical-rl; &amp; item writing-mode: horizontal-tb;</h3>
+
+<pre>grid-template-rows: auto;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: auto;">
+  <div class="horizontalTB" data-expected-width="100">XXXXXXXXXX</div>
+  <div data-expected-width="100"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px);</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: 25px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: 25px;">
+  <div class="horizontalTB" data-expected-width="25">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px);</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px);</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item margin width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0" style="margin: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item margin width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="15" style="margin: 0px 5px;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item padding width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="padding: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item padding width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25" style="padding: 0px 5px;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item border width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="border: solid 5px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item border width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="25" style="border: solid 5px blue;"></div>
+  <div data-expected-width="25"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 25px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 25px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="25"></div>
+</div>
diff --git a/css/css-grid/grid-items/grid-minimum-size-grid-items-024.html b/css/css-grid/grid-items/grid-minimum-size-grid-items-024.html
new file mode 100644
index 0000000..fb706bf
--- /dev/null
+++ b/css/css-grid/grid-items/grid-minimum-size-grid-items-024.html
@@ -0,0 +1,355 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Minimum size of grid items</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css-grid-1/#min-size-auto" title="6.5. Implied Minimum Size of Grid Items">
+<meta name="assert" content="Checks that automatic minimum size is clamped with different column sizes and spaning items.">
+<link rel="stylesheet" href="../support/grid.css">
+<style>
+.grid {
+  border: solid thick;
+  font: 10px/1 Ahem;
+  width: 50px;
+  height: 50px;
+  grid-template-rows: 25px 25px;
+}
+
+.grid > div {
+  grid-column: span 2;
+}
+
+.grid > div:nth-child(1) {
+  color: blue;
+  background: cyan;
+}
+
+.grid > div:nth-child(2) {
+  background: magenta;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.grid')">
+
+<div id="log"></div>
+
+<pre>grid-template-columns: auto auto;</pre>
+
+<div class="grid" style="grid-template-columns: auto auto;">
+  <div data-expected-width="100">XXXXXXXXXX</div>
+  <div data-expected-width="100"></div>
+</div>
+
+<pre>grid-template-columns: 0px 0px;</pre>
+
+<div class="grid" style="grid-template-columns: 0px 0px;">
+  <div data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-columns: 20px 20px;</pre>
+
+<div class="grid" style="grid-template-columns: 20px 20px;">
+  <div data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item margin width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="0" style="margin: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item margin width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="30" style="margin: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item padding width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="10" style="padding: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item padding width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="40" style="padding: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item border width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="10" style="border: solid 5px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item border width: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="40" style="border: solid 5px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<h3>grid content writing-mode: vertical-lr;</h3>
+
+<pre>grid-template-columns: auto auto;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: auto auto;">
+  <div data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-columns: 0px 0px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: 0px 0px;">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: 20px 20px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: 20px 20px;">
+  <div data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item margin height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="30" style="margin: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item padding height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40" style="padding: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item border height: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40" style="border: solid 5px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<h3>grid content writing-mode: vertical-rl;</h3>
+
+<pre>grid-template-columns: auto auto;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: auto auto;">
+  <div data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-columns: 0px 0px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: 0px 0px;">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: 20px 20px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: 20px 20px;">
+  <div data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item margin height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="30" style="margin: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item padding height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40" style="padding: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item border height: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="40" style="border: solid 5px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 0px) minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 0px) minmax(auto, 0px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-columns: minmax(auto, 20px) minmax(auto, 20px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-columns: minmax(auto, 20px) minmax(auto, 20px);">
+  <div data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
diff --git a/css/css-grid/grid-items/grid-minimum-size-grid-items-025.html b/css/css-grid/grid-items/grid-minimum-size-grid-items-025.html
new file mode 100644
index 0000000..722426e
--- /dev/null
+++ b/css/css-grid/grid-items/grid-minimum-size-grid-items-025.html
@@ -0,0 +1,465 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Minimum size of grid items</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css-grid-1/#min-size-auto" title="6.5. Implied Minimum Size of Grid Items">
+<meta name="assert" content="Checks that automatic minimum size is clamped with different row sizes and spaning items.">
+<link rel="stylesheet" href="../support/grid.css">
+<style>
+.grid {
+  border: solid thick;
+  font: 10px/1 Ahem;
+  width: 50px;
+  height: 50px;
+  grid-template-columns: 25px 25px;
+  margin: 50px 0px;
+}
+
+.grid > div {
+  grid-row: span 2;
+}
+
+.grid > div:nth-child(1) {
+  color: blue;
+  background: cyan;
+}
+
+.grid > div:nth-child(2) {
+  background: magenta;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.grid')">
+
+<div id="log"></div>
+
+<h3>item writing-mode: vertical-lr;</h3>
+
+<pre>grid-template-rows: auto auto;</pre>
+
+<div class="grid" style="grid-template-rows: auto auto;">
+  <div class="verticalLR" data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-rows: 0px 0px;</pre>
+
+<div class="grid" style="grid-template-rows: 0px 0px;">
+  <div class="verticalLR" data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-rows: 20px 20px;</pre>
+
+<div class="grid" style="grid-template-rows: 20px 20px;">
+  <div class="verticalLR" data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item margin height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="30" style="margin: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item padding height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="40" style="padding: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item border height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="40" style="border: solid 5px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalLR" data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalLR" data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<h3>item writing-mode: vertical-rl;</h3>
+
+<pre>grid-template-rows: auto auto;</pre>
+
+<div class="grid" style="grid-template-rows: auto auto;">
+  <div class="verticalRL" data-expected-height="100">XXXXXXXXXX</div>
+  <div data-expected-height="100"></div>
+</div>
+
+<pre>grid-template-rows: 0px 0px;</pre>
+
+<div class="grid" style="grid-template-rows: 0px 0px;">
+  <div class="verticalRL" data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-rows: 20px 20px;</pre>
+
+<div class="grid" style="grid-template-rows: 20px 20px;">
+  <div class="verticalRL" data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="0">XXXXXXXXXX</div>
+  <div data-expected-height="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="40">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="10" style="height: 10px;">XXXXXXXXXX</div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item margin height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="0" style="margin: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item margin height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="30" style="margin: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item padding height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="10" style="padding: 5px 0px;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item padding height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="40" style="padding: 5px 0px;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item border height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="10" style="border: solid 5px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item border height: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="40" style="border: solid 5px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="verticalRL" data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item height + margin + border + padding: 10px;</pre>
+
+<div class="grid" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="verticalRL" data-expected-height="8" style="height: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-height="40"></div>
+</div>
+
+<h3>grid container writing-mode: vertical-lr; &amp; item writing-mode: horizontal-tb;</h3>
+
+<pre>grid-template-rows: auto auto;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: auto auto;">
+  <div class="horizontalTB" data-expected-width="100">XXXXXXXXXX</div>
+  <div data-expected-width="100"></div>
+</div>
+
+<pre>grid-template-rows: 0px 0px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: 0px 0px;">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: 20px 20px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: 20px 20px;">
+  <div class="horizontalTB" data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item margin width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0" style="margin: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item margin width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="30" style="margin: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item padding width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="padding: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item padding width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40" style="padding: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item border width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="border: solid 5px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item border width: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40" style="border: solid 5px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalLR" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<h3>grid container writing-mode: vertical-rl; &amp; item writing-mode: horizontal-tb;</h3>
+
+<pre>grid-template-rows: auto auto;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: auto auto;">
+  <div class="horizontalTB" data-expected-width="100">XXXXXXXXXX</div>
+  <div data-expected-width="100"></div>
+</div>
+
+<pre>grid-template-rows: 0px 0px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: 0px 0px;">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: 20px 20px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: 20px 20px;">
+  <div class="horizontalTB" data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0">XXXXXXXXXX</div>
+  <div data-expected-width="0"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="10" style="width: 10px;">XXXXXXXXXX</div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item margin width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="0" style="margin: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item margin width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="30" style="margin: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item padding width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="padding: 0px 5px;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item padding width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40" style="padding: 0px 5px;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item border width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="10" style="border: solid 5px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item border width: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="40" style="border: solid 5px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 0px) minmax(auto, 0px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 0px) minmax(auto, 0px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="10"></div>
+</div>
+
+<pre>grid-template-rows: minmax(auto, 20px) minmax(auto, 20px); item width + margin + border + padding: 10px;</pre>
+
+<div class="grid verticalRL" style="grid-template-rows: minmax(auto, 20px) minmax(auto, 20px);">
+  <div class="horizontalTB" data-expected-width="8" style="width: 4px; margin: 1px; padding: 1px; border: solid 1px blue;"></div>
+  <div data-expected-width="40"></div>
+</div>
diff --git a/css/css-layout-api/box-tree-registered-ref.html b/css/css-layout-api/box-tree-registered-ref.html
new file mode 100644
index 0000000..0ba9862
--- /dev/null
+++ b/css/css-layout-api/box-tree-registered-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+.container {
+  margin: 20px 0;
+  border: solid 2px;
+  width: 100px;
+}
+
+.pink {
+  background: hotpink;
+}
+</style>
+
+<div class="container">
+  <div class="pink" style="width: 50px; height: 100px;"></div>
+</div>
+
+<div class="container">
+  <div class="pink" style="width: 100px; height: 100px;"></div>
+</div>
diff --git a/css/css-layout-api/box-tree-registered.https.html b/css/css-layout-api/box-tree-registered.https.html
new file mode 100644
index 0000000..eb3fb67
--- /dev/null
+++ b/css/css-layout-api/box-tree-registered.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layout-api-box-tree">
+<link rel="match" href="box-tree-registered-ref.html">
+<meta name="assert" content="This test checks that a registered layout() has children which are blockified and new formatting contexts." />
+<style>
+@supports (display: layout(registered)) {
+  .test {
+    display: layout(registered);
+  }
+}
+
+.container {
+  margin: 20px 0;
+  border: solid 2px;
+  width: 100px;
+}
+
+.float {
+  float: left;
+  width: 50%;
+  height: 50px;
+}
+
+.pink {
+  background: hotpink;
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="container">
+  <!-- This tests that the "layout()" grandchildren floats within the don't interact with each other. -->
+  <div class="test">
+    <div class="inflow">
+      <div class="float pink"></div>
+    </div>
+    <div class="inflow">
+      <div class="float pink"></div>
+    </div>
+  </div>
+</div>
+
+<div class="container">
+  <!-- This tests that the "layout()" children margins interact as if they are new formatting contexts. -->
+  <div class="test">
+    <div class="inflow pink">
+      <div style="margin-bottom: 50px;"></div>
+    </div>
+    <div class="inflow pink">
+      <div style="margin-top: 50px;"></div>
+    </div>
+  </div>
+</div>
+
+<script id="code" type="text/worklet">
+registerLayout('registered', class {
+  *intrinsicSizes() {}
+  *layout() {}
+});
+</script>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, document.getElementById('code').textContent);
+</script>
+</html>
diff --git a/css/css-layout-api/box-tree-unregistered-ref.html b/css/css-layout-api/box-tree-unregistered-ref.html
new file mode 100644
index 0000000..fc43ad2
--- /dev/null
+++ b/css/css-layout-api/box-tree-unregistered-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<style>
+.container {
+  margin: 20px 0;
+  border: solid 2px;
+  width: 100px;
+}
+
+.inflow {
+  height: 40px;
+}
+</style>
+
+<div class="container">
+  <div class="inflow"></div>
+  <div class="inflow" style="background: hotpink"></div>
+  <div class="inflow"></div>
+</div>
+
+<div class="container">
+  <div class="inflow"></div>
+  <div class="inflow" style="background: hotpink"></div>
+  <div class="inflow"></div>
+</div>
+
+<div class="container">
+  <div class="inflow" style="background: hotpink"></div>
+  <div class="inflow" style="background: green"></div>
+</div>
diff --git a/css/css-layout-api/box-tree-unregistered.https.html b/css/css-layout-api/box-tree-unregistered.https.html
new file mode 100644
index 0000000..48d7aa7
--- /dev/null
+++ b/css/css-layout-api/box-tree-unregistered.https.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layout-api-box-tree">
+<link rel="match" href="box-tree-unregistered-ref.html">
+<meta name="assert" content="This test checks that an unregistered layout() works the same as a normal flow-root." />
+<style>
+@supports (display: layout(unregistered)) {
+  .test {
+    display: layout(unregistered);
+  }
+}
+
+.container {
+  margin: 20px 0;
+  border: solid 2px;
+  width: 100px;
+}
+
+.float {
+  float: left;
+  width: 100%;
+  height: 40px;
+  background: hotpink;
+}
+
+.inflow {
+  height: 40px;
+  background: hotpink;
+}
+</style>
+
+<div class="container">
+  <!-- This tests that the "layout()" margins don't collapse with its children. -->
+  <div class="test" style="margin: 20px 0">
+    <div class="inflow" style="margin: 20px 0"></div>
+  </div>
+</div>
+
+<div class="container">
+  <!-- This tests that the "layout()" grows to fit child floats. -->
+  <div class="test">
+    <div class="float" style="margin: 40px 0"></div>
+  </div>
+</div>
+
+<div class="container">
+  <!-- This tests that a float does not intrude into "layout()" box. -->
+  <div class="float"></div>
+  <div class="test" style="width: 100%; height: 40px; background: green;"></div>
+</div>
diff --git a/css/css-paint-api/background-image-alpha.https.html b/css/css-paint-api/background-image-alpha.https.html
index de8a0d7..53d869d 100644
--- a/css/css-paint-api/background-image-alpha.https.html
+++ b/css/css-paint-api/background-image-alpha.https.html
@@ -21,7 +21,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="background">
   <div id="canvas-opaque" class="container"></div>
@@ -49,7 +49,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/background-image-multiple.https.html b/css/css-paint-api/background-image-multiple.https.html
index 7fb5751..79ff883 100644
--- a/css/css-paint-api/background-image-multiple.https.html
+++ b/css/css-paint-api/background-image-multiple.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -27,7 +27,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/background-image-tiled.https.html b/css/css-paint-api/background-image-tiled.https.html
index 0497acf..8498c82 100644
--- a/css/css-paint-api/background-image-tiled.https.html
+++ b/css/css-paint-api/background-image-tiled.https.html
@@ -21,7 +21,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="one"></div>
 <div id="two"></div>
@@ -38,7 +38,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-background-image-001.https.html b/css/css-paint-api/geometry-background-image-001.https.html
index d1207e0..601d418 100644
--- a/css/css-paint-api/geometry-background-image-001.https.html
+++ b/css/css-paint-api/geometry-background-image-001.https.html
@@ -13,7 +13,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -28,7 +28,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-background-image-002.https.html b/css/css-paint-api/geometry-background-image-002.https.html
index 47455ba..1d57073 100644
--- a/css/css-paint-api/geometry-background-image-002.https.html
+++ b/css/css-paint-api/geometry-background-image-002.https.html
@@ -13,7 +13,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -28,7 +28,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-background-image-tiled-001.https.html b/css/css-paint-api/geometry-background-image-tiled-001.https.html
index 5cf8eb7..8e28b54 100644
--- a/css/css-paint-api/geometry-background-image-tiled-001.https.html
+++ b/css/css-paint-api/geometry-background-image-tiled-001.https.html
@@ -14,7 +14,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -30,7 +30,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-background-image-tiled-002.https.html b/css/css-paint-api/geometry-background-image-tiled-002.https.html
index 491abd1..9248e38 100644
--- a/css/css-paint-api/geometry-background-image-tiled-002.https.html
+++ b/css/css-paint-api/geometry-background-image-tiled-002.https.html
@@ -14,7 +14,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -29,7 +29,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-background-image-tiled-003.https.html b/css/css-paint-api/geometry-background-image-tiled-003.https.html
index 9a29c30..5b6b6c7 100644
--- a/css/css-paint-api/geometry-background-image-tiled-003.https.html
+++ b/css/css-paint-api/geometry-background-image-tiled-003.https.html
@@ -14,7 +14,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -29,7 +29,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-border-image-001.https.html b/css/css-paint-api/geometry-border-image-001.https.html
index 54249d3..3fb4643 100644
--- a/css/css-paint-api/geometry-border-image-001.https.html
+++ b/css/css-paint-api/geometry-border-image-001.https.html
@@ -15,7 +15,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -34,7 +34,7 @@
 
 <script>
     document.getElementById('canvas-geometry').style.borderWidth = '10px';
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-border-image-002.https.html b/css/css-paint-api/geometry-border-image-002.https.html
index 4759886..26e24bb 100644
--- a/css/css-paint-api/geometry-border-image-002.https.html
+++ b/css/css-paint-api/geometry-border-image-002.https.html
@@ -15,7 +15,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -34,7 +34,7 @@
 
 <script>
     document.getElementById('canvas-geometry').style.borderImageOutset = '20px';
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-border-image-003.https.html b/css/css-paint-api/geometry-border-image-003.https.html
index 1ce6ac9..a26f2b7 100644
--- a/css/css-paint-api/geometry-border-image-003.https.html
+++ b/css/css-paint-api/geometry-border-image-003.https.html
@@ -15,7 +15,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -34,7 +34,7 @@
 
 <script>
     document.getElementById('canvas-geometry').style.borderImageOutset = '10px';
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-border-image-004.https.html b/css/css-paint-api/geometry-border-image-004.https.html
index b15b66d..60db7ff 100644
--- a/css/css-paint-api/geometry-border-image-004.https.html
+++ b/css/css-paint-api/geometry-border-image-004.https.html
@@ -15,7 +15,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -35,7 +35,7 @@
 <script>
     document.getElementById('canvas-geometry').style.borderWidth = '10px';
     document.getElementById('canvas-geometry').style.borderImageOutset = '10px';
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/geometry-with-float-size.https.html b/css/css-paint-api/geometry-with-float-size.https.html
index 6cd3eca..65477e4 100644
--- a/css/css-paint-api/geometry-with-float-size.https.html
+++ b/css/css-paint-api/geometry-with-float-size.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -29,7 +29,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/hidpi/device-pixel-ratio.https.html b/css/css-paint-api/hidpi/device-pixel-ratio.https.html
index fb62c23..46a9aa2 100644
--- a/css/css-paint-api/hidpi/device-pixel-ratio.https.html
+++ b/css/css-paint-api/hidpi/device-pixel-ratio.https.html
@@ -13,7 +13,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <p>This test ensures that the PaintWorkletGlobalScope.devicePixelRatio returns
 the correct value, which should be identical as window.devicePixelRatio. To
@@ -33,7 +33,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/invalid-image-constructor-error.https.html b/css/css-paint-api/invalid-image-constructor-error.https.html
index 022b915..439ff8b 100644
--- a/css/css-paint-api/invalid-image-constructor-error.https.html
+++ b/css/css-paint-api/invalid-image-constructor-error.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -33,7 +33,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 
 </body>
diff --git a/css/css-paint-api/invalid-image-paint-error.https.html b/css/css-paint-api/invalid-image-paint-error.https.html
index c185d89..2806ad9 100644
--- a/css/css-paint-api/invalid-image-paint-error.https.html
+++ b/css/css-paint-api/invalid-image-paint-error.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -30,7 +30,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/invalid-image-pending-script.https.html b/css/css-paint-api/invalid-image-pending-script.https.html
index e19fa41..4347a00 100644
--- a/css/css-paint-api/invalid-image-pending-script.https.html
+++ b/css/css-paint-api/invalid-image-pending-script.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -26,7 +26,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/overdraw.https.html b/css/css-paint-api/overdraw.https.html
index f95eeb5..5be26f7 100644
--- a/css/css-paint-api/overdraw.https.html
+++ b/css/css-paint-api/overdraw.https.html
@@ -10,7 +10,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -24,7 +24,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint-arguments.https.html b/css/css-paint-api/paint-arguments.https.html
index f4ae452..abfb2a6 100644
--- a/css/css-paint-api/paint-arguments.https.html
+++ b/css/css-paint-api/paint-arguments.https.html
@@ -21,7 +21,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 
 <div id="background">
@@ -43,7 +43,7 @@
 </script>
 
 <script>
-  importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+  importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint-function-arguments.https.html b/css/css-paint-api/paint-function-arguments.https.html
index d49f3f2..d87b0b8 100644
--- a/css/css-paint-api/paint-function-arguments.https.html
+++ b/css/css-paint-api/paint-function-arguments.https.html
@@ -21,7 +21,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 
 <div id="background">
@@ -43,7 +43,7 @@
 </script>
 
 <script>
-  importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+  importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-composite.https.html b/css/css-paint-api/paint2d-composite.https.html
index ba88f92..80e94e4 100644
--- a/css/css-paint-api/paint2d-composite.https.html
+++ b/css/css-paint-api/paint2d-composite.https.html
@@ -19,7 +19,7 @@
     #xor { background-image: paint(xor); }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="source-over"></div>
 <div id="source-in"></div>
@@ -67,7 +67,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-filter.https.html b/css/css-paint-api/paint2d-filter.https.html
index 560b1b2..d0c4539 100644
--- a/css/css-paint-api/paint2d-filter.https.html
+++ b/css/css-paint-api/paint2d-filter.https.html
@@ -24,7 +24,7 @@
     #filter-url { background-image: paint(filter-url); }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="filter-none"></div>
 <div id="filter-blur-10px"></div>
@@ -100,7 +100,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-gradient.https.html b/css/css-paint-api/paint2d-gradient.https.html
index b936430..892a791 100644
--- a/css/css-paint-api/paint2d-gradient.https.html
+++ b/css/css-paint-api/paint2d-gradient.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -34,7 +34,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-image.https.html b/css/css-paint-api/paint2d-image.https.html
index ba89430..6fce4b6 100644
--- a/css/css-paint-api/paint2d-image.https.html
+++ b/css/css-paint-api/paint2d-image.https.html
@@ -10,7 +10,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <div id="output"></div>
 
 <script id="code" type="text/worklet">
@@ -25,6 +25,6 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </html>
diff --git a/css/css-paint-api/paint2d-paths.https.html b/css/css-paint-api/paint2d-paths.https.html
index 55a01b4..091f548 100644
--- a/css/css-paint-api/paint2d-paths.https.html
+++ b/css/css-paint-api/paint2d-paths.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-rects.https.html b/css/css-paint-api/paint2d-rects.https.html
index 24247da..2494272 100644
--- a/css/css-paint-api/paint2d-rects.https.html
+++ b/css/css-paint-api/paint2d-rects.https.html
@@ -10,7 +10,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -30,7 +30,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-shadows.https.html b/css/css-paint-api/paint2d-shadows.https.html
index ad0a1aa..98dcfbc 100644
--- a/css/css-paint-api/paint2d-shadows.https.html
+++ b/css/css-paint-api/paint2d-shadows.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -33,7 +33,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/paint2d-transform.https.html b/css/css-paint-api/paint2d-transform.https.html
index f5b6aa8..c91b500 100644
--- a/css/css-paint-api/paint2d-transform.https.html
+++ b/css/css-paint-api/paint2d-transform.https.html
@@ -9,7 +9,7 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="output"></div>
 
@@ -32,7 +32,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-001.https.html b/css/css-paint-api/parse-input-arguments-001.https.html
index 931a55a..4356ce6 100644
--- a/css/css-paint-api/parse-input-arguments-001.https.html
+++ b/css/css-paint-api/parse-input-arguments-001.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-002.https.html b/css/css-paint-api/parse-input-arguments-002.https.html
index c07c0c0..6fc16c7 100644
--- a/css/css-paint-api/parse-input-arguments-002.https.html
+++ b/css/css-paint-api/parse-input-arguments-002.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-003.https.html b/css/css-paint-api/parse-input-arguments-003.https.html
index ff388de..44fd850 100644
--- a/css/css-paint-api/parse-input-arguments-003.https.html
+++ b/css/css-paint-api/parse-input-arguments-003.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-004.https.html b/css/css-paint-api/parse-input-arguments-004.https.html
index 8d5b8d7..138a790 100644
--- a/css/css-paint-api/parse-input-arguments-004.https.html
+++ b/css/css-paint-api/parse-input-arguments-004.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -43,7 +43,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-005.https.html b/css/css-paint-api/parse-input-arguments-005.https.html
index b726c22..c12e00c 100644
--- a/css/css-paint-api/parse-input-arguments-005.https.html
+++ b/css/css-paint-api/parse-input-arguments-005.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -39,7 +39,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-006.https.html b/css/css-paint-api/parse-input-arguments-006.https.html
index 7d8504c..664b7a8 100644
--- a/css/css-paint-api/parse-input-arguments-006.https.html
+++ b/css/css-paint-api/parse-input-arguments-006.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -38,7 +38,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-007.https.html b/css/css-paint-api/parse-input-arguments-007.https.html
index a59ac93..53f245b 100644
--- a/css/css-paint-api/parse-input-arguments-007.https.html
+++ b/css/css-paint-api/parse-input-arguments-007.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-008.https.html b/css/css-paint-api/parse-input-arguments-008.https.html
index 2d6df23..1914e3c 100644
--- a/css/css-paint-api/parse-input-arguments-008.https.html
+++ b/css/css-paint-api/parse-input-arguments-008.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-009.https.html b/css/css-paint-api/parse-input-arguments-009.https.html
index 03c7c69..21d004d 100644
--- a/css/css-paint-api/parse-input-arguments-009.https.html
+++ b/css/css-paint-api/parse-input-arguments-009.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -40,7 +40,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-010.https.html b/css/css-paint-api/parse-input-arguments-010.https.html
index 0bd6768..d923479 100644
--- a/css/css-paint-api/parse-input-arguments-010.https.html
+++ b/css/css-paint-api/parse-input-arguments-010.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -40,7 +40,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-011.https.html b/css/css-paint-api/parse-input-arguments-011.https.html
index e772a50..6cea438 100644
--- a/css/css-paint-api/parse-input-arguments-011.https.html
+++ b/css/css-paint-api/parse-input-arguments-011.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -38,7 +38,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-012.https.html b/css/css-paint-api/parse-input-arguments-012.https.html
index 422b45f..938150a 100644
--- a/css/css-paint-api/parse-input-arguments-012.https.html
+++ b/css/css-paint-api/parse-input-arguments-012.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-013.https.html b/css/css-paint-api/parse-input-arguments-013.https.html
index cd62879..37e3eb435 100644
--- a/css/css-paint-api/parse-input-arguments-013.https.html
+++ b/css/css-paint-api/parse-input-arguments-013.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -38,7 +38,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-014.https.html b/css/css-paint-api/parse-input-arguments-014.https.html
index 39aee6a..690e488 100644
--- a/css/css-paint-api/parse-input-arguments-014.https.html
+++ b/css/css-paint-api/parse-input-arguments-014.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -40,7 +40,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-015.https.html b/css/css-paint-api/parse-input-arguments-015.https.html
index c2e1844..fba7671 100644
--- a/css/css-paint-api/parse-input-arguments-015.https.html
+++ b/css/css-paint-api/parse-input-arguments-015.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -43,7 +43,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-016.https.html b/css/css-paint-api/parse-input-arguments-016.https.html
index 4bea1b4..8c9d05d 100644
--- a/css/css-paint-api/parse-input-arguments-016.https.html
+++ b/css/css-paint-api/parse-input-arguments-016.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -42,7 +42,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-017.https.html b/css/css-paint-api/parse-input-arguments-017.https.html
index 38f9f43..0d14fe74 100644
--- a/css/css-paint-api/parse-input-arguments-017.https.html
+++ b/css/css-paint-api/parse-input-arguments-017.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -41,7 +41,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-018.https.html b/css/css-paint-api/parse-input-arguments-018.https.html
index 9d03a8d..541332a 100644
--- a/css/css-paint-api/parse-input-arguments-018.https.html
+++ b/css/css-paint-api/parse-input-arguments-018.https.html
@@ -13,7 +13,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <p>This test result should show a rect with black border, where the rect is
 filled with green on the lower right corner. The registerPaint('failureIndicator')
@@ -63,7 +63,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-019.https.html b/css/css-paint-api/parse-input-arguments-019.https.html
index 76627fb..707a0c6 100644
--- a/css/css-paint-api/parse-input-arguments-019.https.html
+++ b/css/css-paint-api/parse-input-arguments-019.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -41,7 +41,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-020.https.html b/css/css-paint-api/parse-input-arguments-020.https.html
index b582555..fe8dbad 100644
--- a/css/css-paint-api/parse-input-arguments-020.https.html
+++ b/css/css-paint-api/parse-input-arguments-020.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -40,7 +40,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-021.https.html b/css/css-paint-api/parse-input-arguments-021.https.html
index 666d4c4..0c3a596 100644
--- a/css/css-paint-api/parse-input-arguments-021.https.html
+++ b/css/css-paint-api/parse-input-arguments-021.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -43,7 +43,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/parse-input-arguments-022.https.html b/css/css-paint-api/parse-input-arguments-022.https.html
index 3c5d9f9..50aaa6b 100644
--- a/css/css-paint-api/parse-input-arguments-022.https.html
+++ b/css/css-paint-api/parse-input-arguments-022.https.html
@@ -12,7 +12,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -41,7 +41,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/registered-properties-in-custom-paint.https.html b/css/css-paint-api/registered-properties-in-custom-paint.https.html
index c446347..00b0368 100644
--- a/css/css-paint-api/registered-properties-in-custom-paint.https.html
+++ b/css/css-paint-api/registered-properties-in-custom-paint.https.html
@@ -14,7 +14,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -56,7 +56,7 @@
     CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: '0px'});
     CSS.registerProperty({name: '--length-initial', syntax: '<length>', initialValue: '20px'});
     CSS.registerProperty({name: '--number', syntax: '<number>', initialValue: '0'});
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/style-background-image.https.html b/css/css-paint-api/style-background-image.https.html
index 7853607..6eeaf3c 100644
--- a/css/css-paint-api/style-background-image.https.html
+++ b/css/css-paint-api/style-background-image.https.html
@@ -14,7 +14,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body>
 <div id="canvas-geometry" class="container"></div>
 
@@ -56,7 +56,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/style-before-pseudo.https.html b/css/css-paint-api/style-before-pseudo.https.html
index 5249f58..bf3f69f 100644
--- a/css/css-paint-api/style-before-pseudo.https.html
+++ b/css/css-paint-api/style-before-pseudo.https.html
@@ -18,7 +18,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body style="font: 10px/1 Ahem;">
 <div></div>
 
@@ -57,7 +57,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/style-first-letter-pseudo.https.html b/css/css-paint-api/style-first-letter-pseudo.https.html
index 3cdffb4..b8a22ce 100644
--- a/css/css-paint-api/style-first-letter-pseudo.https.html
+++ b/css/css-paint-api/style-first-letter-pseudo.https.html
@@ -15,7 +15,7 @@
 }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
+<script src="/common/worklet-reftest.js"></script>
 <body style="font: 10px/1 Ahem;">
 <div>ppp</div>
 
@@ -51,7 +51,7 @@
 </script>
 
 <script>
-    importPaintWorkletAndTerminateTestAfterAsyncPaint(document.getElementById('code').textContent);
+    importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
 </script>
 </body>
 </html>
diff --git a/css/css-paint-api/valid-image-after-load.https.html b/css/css-paint-api/valid-image-after-load.https.html
index 73557d2..44612f9 100644
--- a/css/css-paint-api/valid-image-after-load.https.html
+++ b/css/css-paint-api/valid-image-after-load.https.html
@@ -9,7 +9,6 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
 <body>
 <div id="output"></div>
 
diff --git a/css/css-paint-api/valid-image-before-load.https.html b/css/css-paint-api/valid-image-before-load.https.html
index 483d16c..7738d9d 100644
--- a/css/css-paint-api/valid-image-before-load.https.html
+++ b/css/css-paint-api/valid-image-before-load.https.html
@@ -9,7 +9,6 @@
     }
 </style>
 <script src="/common/reftest-wait.js"></script>
-<script src="/common/css-paint-tests.js"></script>
 <body>
 <div id="output"></div>
 
diff --git a/css/css-pseudo/first-letter-property-whitelist.html b/css/css-pseudo/first-letter-property-whitelist.html
new file mode 100644
index 0000000..073e554
--- /dev/null
+++ b/css/css-pseudo/first-letter-property-whitelist.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<title>CSS Test: Properties allowed on ::first-letter pseudo elements</title>
+<link rel="author" title="Chris Nardi" href="mailto:cnardi@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#first-letter-styling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#target::first-letter {}
+#target { visibility: hidden; }
+</style>
+<div id="target">text</div>
+<script>
+'use strict';
+var style;
+const target = document.querySelector("#target");
+var defaultComputedStyle = getComputedStyle(target);
+
+test(function() {
+  var styleRule = document.styleSheets[0].cssRules[0];
+  assert_equals(styleRule.selectorText, '#target::first-letter', 'make sure we have the correct style rule');
+  style = styleRule.style;
+}, 'pre test setup');
+
+var validProperties = {
+  backgroundAttachment: 'fixed',
+  backgroundBlendMode: 'hue',
+  backgroundClip: 'padding-box',
+  backgroundColor: 'rgb(10, 20, 30)',
+  backgroundImage: 'linear-gradient(black, white)',
+  backgroundOrigin: 'border-box',
+  backgroundPosition: 'left 10px top 20px',
+  backgroundRepeat: 'no-repeat',
+  backgroundSize: '10px 20px',
+  border: '40px dotted rgb(10, 20, 30)',
+  borderImage: 'linear-gradient(black, white) 10% / 20 / 30px repeat',
+  borderRadius: '10px 20px',
+  boxShadow: 'rgb(10, 20, 30) 10px 20px 30px 40px inset',
+  color: 'rgba(10, 20, 30, 0.4)',
+  float: 'right',
+  font: 'italic small-caps 900 normal 10px / 20px sans-serif',
+  fontFeatureSettings: '"vert" 2',
+  fontSizeAdjust: '0.5',
+  fontKerning: 'none',
+  fontVariationSettings: '"XHGT" 0.7',
+  letterSpacing: '12px',
+  margin: '10px 20px 30px 40px',
+  padding: '10px 20px 30px 40px',
+  opacity: '0.5',
+  textDecoration: 'overline wavy rgb(10, 20, 30)',
+  textJustify: 'inter-word',
+  textShadow: 'rgb(10, 20, 30) 10px 20px 30px',
+  textTransform: 'capitalize',
+  textUnderlinePosition: 'under',
+  verticalAlign: '12%',
+  wordSpacing: '12px'
+};
+
+var invalidProperties = {
+  transition: 'transform 1s',
+  transform: 'rotate(45deg)',
+  wordBreak: 'break-all'
+};
+
+function testFirstLetterProperty(property, rule, expected, reason) {
+  test(function() {
+    style[property] = "";
+    style[property] = rule;
+    assert_equals(getComputedStyle(target, '::first-letter')[property], expected);
+    style[property] = "";
+  }, reason);
+}
+
+for (var property in validProperties) {
+  testFirstLetterProperty(property, validProperties[property], validProperties[property],
+                          "Whitelisted property " + property + " should be applied to first-letter pseudo elements.");
+}
+
+for (var property in invalidProperties) {
+  testFirstLetterProperty(property, invalidProperties[property], defaultComputedStyle[property],
+                          "Non-whitelisted property " + property + " should not be applied to first-letter pseudo elements.");
+}
+</script>
diff --git a/css/css-scoping/slotted-link.html b/css/css-scoping/slotted-link.html
new file mode 100644
index 0000000..c06c961
--- /dev/null
+++ b/css/css-scoping/slotted-link.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Scoping: ::slotted :link</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#slotted-pseudo">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="host"><a id="a" href="#notvisited">This link should be green.</a></div>
+<script>
+  let root = host.attachShadow({mode:"open"});
+  root.innerHTML = `
+  <style>
+    ::slotted(:link) { color:green }
+    ::slotted(:visited) { color:red }
+  </style>
+  <slot></slot>`;
+
+  test(() => {
+    assert_equals(getComputedStyle(a).color, "rgb(0, 128, 0)", "Unvisited link should be green.");
+  }, "Check that we match :link and not :visited for slotted anchor.");
+</script>
diff --git a/css/css-tables/internal-containing-block-001.html b/css/css-tables/internal-containing-block-001.html
new file mode 100644
index 0000000..a874548
--- /dev/null
+++ b/css/css-tables/internal-containing-block-001.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Grogan" href="dgrogan@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#abspos-boxes-in-table-internal">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#valdef-position-absolute">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<link rel="bookmark" href="https://bugs.chromium.org/p/chromium/issues/detail?id=798164" />
+<meta name="flags" content="" />
+<meta name="assert" content="A table-row with position:relative establishes a
+containing block for a position:absolute descendent of a table-cell, and the
+descendent is positioned correctly." />
+<title>
+relpos table-row establishes containing block for abspos
+</title>
+
+<style>
+tr {
+  position: relative;
+}
+
+div.abspos {
+  position: absolute;
+  height: 50px;
+  width: 100px;
+  background: green;
+}
+
+div.static {
+  height: 50px;
+  width: 100px;
+  background:red;
+}
+
+td, tr {
+  margin:0px;
+  padding:0px;
+}
+
+table {
+  border-collapse:collapse;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.
+</p>
+
+<table>
+  <tr>
+    <td>
+      <div class="abspos"></div>
+      <div class="static"></div>
+    </td>
+  </tr>
+  <tr>
+    <td>
+      <div class="abspos"></div>
+      <div class="static"></div>
+    </td>
+  </tr>
+</table>
diff --git a/css/css-transforms/animation/scale-interpolation.html b/css/css-transforms/animation/scale-interpolation.html
index da25d3a..942b80a 100644
--- a/css/css-transforms/animation/scale-interpolation.html
+++ b/css/css-transforms/animation/scale-interpolation.html
@@ -28,13 +28,13 @@
       test_interpolation({
         property: 'scale',
         from: '26 17 9',
-        to: '2',
+        to: '2 1',
       }, [
         {at: -1, expect: '50 33 17'},
         {at: 0, expect: '26 17 9'},
         {at: 0.125, expect: '23 15 8'},
         {at: 0.875, expect: '5 3 2'},
-        {at: 1, expect: '2'},
+        {at: 1, expect: '2 1'},
         {at: 2, expect: '-22 -15 -7'}
       ]);
 
diff --git a/css/css-transforms/css-transform-property-existence.html b/css/css-transforms/css-transform-property-existence.html
index 75e1a1b..5885db2 100644
--- a/css/css-transforms/css-transform-property-existence.html
+++ b/css/css-transforms/css-transform-property-existence.html
@@ -14,7 +14,7 @@
     <div id="log"></div>
     <script>
       test(function() {
-        assert_not_equals(document.getElementById("test").style.transfor, undefined, "expect transform is not undefined");
+        assert_not_equals(document.getElementById("test").style.transform, undefined, "expect transform is not undefined");
       }, "Check the existence of transform.");
 
       test(function() {
diff --git a/css/css-transforms/individual-transform/individual-transform-1-ref.html b/css/css-transforms/individual-transform/individual-transform-1-ref.html
index 8a568d2..9271c77 100644
--- a/css/css-transforms/individual-transform/individual-transform-1-ref.html
+++ b/css/css-transforms/individual-transform/individual-transform-1-ref.html
@@ -24,7 +24,7 @@
         left: 100px;
         width: 50px;
         height: 100px;
-        transform: scaleX(2);
+        transform: scale(2, 2);
       }
       .translate_1 {
         left: 150px;
diff --git a/css/css-transforms/parsing/rotate-parsing-invalid.html b/css/css-transforms/parsing/rotate-parsing-invalid.html
index 2597a42..8326db3 100644
--- a/css/css-transforms/parsing/rotate-parsing-invalid.html
+++ b/css/css-transforms/parsing/rotate-parsing-invalid.html
@@ -17,6 +17,12 @@
 test_invalid_value("rotate", "100 400deg");
 test_invalid_value("rotate", "100 200 400deg");
 test_invalid_value("rotate", "100 200 300 500 400deg");
+
+test_invalid_value("rotate", "x y 45deg");
+test_invalid_value("rotate", "45deg x y");
+test_invalid_value("rotate", "z");
+test_invalid_value("rotate", "1 2");
+test_invalid_value("rotate", "1 2 3");
 </script>
 </body>
 </html>
diff --git a/css/css-transforms/parsing/rotate-parsing-valid.html b/css/css-transforms/parsing/rotate-parsing-valid.html
index a33f0fd..b2e5749 100644
--- a/css/css-transforms/parsing/rotate-parsing-valid.html
+++ b/css/css-transforms/parsing/rotate-parsing-valid.html
@@ -17,6 +17,16 @@
 test_valid_value("rotate", "0deg");
 
 test_valid_value("rotate", "100 200 300 400grad");
+test_valid_value("rotate", "400grad 100 200 300", "100 200 300 400grad");
+
+test_valid_value("rotate", "x 400grad", "1 0 0 400grad");
+test_valid_value("rotate", "400grad x", "1 0 0 400grad");
+
+test_valid_value("rotate", "y 400grad", "0 1 0 400grad");
+test_valid_value("rotate", "400grad y", "0 1 0 400grad");
+
+test_valid_value("rotate", "z 400grad", "0 0 1 400grad");
+test_valid_value("rotate", "400grad z", "0 0 1 400grad");
 </script>
 </body>
 </html>
diff --git a/css/css-transforms/transforms-support-calc.html b/css/css-transforms/transforms-support-calc.html
index 19bacd9..71a046f 100644
--- a/css/css-transforms/transforms-support-calc.html
+++ b/css/css-transforms/transforms-support-calc.html
@@ -31,7 +31,7 @@
 
 test(function(){
   target.style = 'translate: calc(30px + 20%) calc(-200px + 100%);';
-  assert_equals(getComputedStyle(target).translate, '90px 0px');
+  assert_equals(getComputedStyle(target).translate, 'calc(30px + 20%) calc(-200px + 100%)');
 }, 'translate supports calc');
 
 test(function(){
diff --git a/css/css-transforms/translate-getComputedStyle.html b/css/css-transforms/translate-getComputedStyle.html
new file mode 100644
index 0000000..1e6759d
--- /dev/null
+++ b/css/css-transforms/translate-getComputedStyle.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Transform Module Level 2: translate getComputedStyle</title>
+  <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+  <link rel="help" href="https://drafts.csswg.org/css-transforms-2/#propdef-translate">
+  <meta name="assert" content="translate computed style does not resolve percentages.">
+  <style type="text/css">
+    #container {
+      transform-style: preserve-3d;;
+    }
+    #first {
+      font-size: 10px;
+      translate: 10px 2em;
+    }
+    #second {
+      translate: 30% 40% 50px;
+    }
+    #third {
+      font-size: 10px;
+      width: 98px;
+      height: 76px;
+      translate: calc(7em + 80%) -9em;
+    }
+  </style>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+  <div id="container">
+    <div id="first"></div>
+    <div id="second"></div>
+    <div id="third"></div>
+  </div>
+  <script>
+    'use strict';
+    function getTranslateFor(id) {
+      return window.getComputedStyle(document.getElementById(id)).getPropertyValue("translate");
+    }
+
+    test(function() {
+      assert_equals(getTranslateFor("first"), "10px 20px");
+      assert_equals(getTranslateFor("second"), "30% 40% 50px");
+      assert_equals(getTranslateFor("third"), "calc(70px + 80%) -90px");
+    }, "computed style for translate");
+  </script>
+</body>
+</html>
diff --git a/css/css-typed-om/resources/testhelper.js b/css/css-typed-om/resources/testhelper.js
index f7c7e03..91af64c 100644
--- a/css/css-typed-om/resources/testhelper.js
+++ b/css/css-typed-om/resources/testhelper.js
@@ -54,6 +54,12 @@
       assert_style_value_equals(a.ax, b.ax);
       assert_style_value_equals(a.ay, b.ay);
       break;
+    case 'CSSSkewX':
+      assert_style_value_equals(a.ax, b.ax);
+      break;
+    case 'CSSSkewY':
+      assert_style_value_equals(a.ay, b.ay);
+      break;
     case 'CSSPerspective':
       assert_style_value_equals(a.length, b.length);
       break;
diff --git a/css/css-typed-om/stylevalue-normalization/normalize-tokens.tentative.html b/css/css-typed-om/stylevalue-normalization/normalize-tokens.tentative.html
index 3311380..5e58bea 100644
--- a/css/css-typed-om/stylevalue-normalization/normalize-tokens.tentative.html
+++ b/css/css-typed-om/stylevalue-normalization/normalize-tokens.tentative.html
@@ -29,22 +29,22 @@
   {
     value: 'var(--A, 1em)',
     expectedResult: [
-      new CSSVariableReferenceValue('--A', new CSSUnparsedValue(' 1em')),
+      new CSSVariableReferenceValue('--A', new CSSUnparsedValue([' 1em'])),
     ]
   },
   {
     value: 'var(--A, var(--B))',
     expectedResult: [
-      new CSSVariableReferenceValue('--A', new CSSUnparsedValue(' ', new CSSVariableReferenceValue('--B'))),
+      new CSSVariableReferenceValue('--A', new CSSUnparsedValue([' ', new CSSVariableReferenceValue('--B')])),
     ]
   },
   {
     value: 'calc(42px + var(--foo, 15em) + var(--bar, var(--far) + 15px))',
     expectedResult: [
       'calc(42px + ',
-      new CSSVariableReferenceValue('--foo', new CSSUnparsedValue(' 15em')),
+      new CSSVariableReferenceValue('--foo', new CSSUnparsedValue([' 15em'])),
       ' + ',
-      new CSSVariableReferenceValue('--bar', new CSSUnparsedValue(' ', new CSSVariableReferenceValue('--far'), ' + 15px')),
+      new CSSVariableReferenceValue('--bar', new CSSUnparsedValue([' ', new CSSVariableReferenceValue('--far'), ' + 15px'])),
       ')',
     ]
   },
@@ -52,11 +52,11 @@
 
 for (const {value, expectedResult} of gTestCases) {
   test(t => {
-    assert_string_normalizes_to(t, 'color', value, new CSSUnparsedValue(...expectedResult));
+    assert_string_normalizes_to(t, 'color', value, new CSSUnparsedValue(expectedResult));
   }, 'Normalizing "' + value + '" on a CSS property returns correct CSSUnparsedValue');
 
   test(t => {
-    assert_string_normalizes_to(t, '--X', value, new CSSUnparsedValue(...expectedResult));
+    assert_string_normalizes_to(t, '--X', value, new CSSUnparsedValue(expectedResult));
   }, 'Normalizing "' + value + '" on a custom property returns correct CSSUnparsedValue');
 }
 
diff --git a/css/css-typed-om/stylevalue-normalization/transformvalue-normalization.tentative.html b/css/css-typed-om/stylevalue-normalization/transformvalue-normalization.tentative.html
index 25b980b..9157ae8 100644
--- a/css/css-typed-om/stylevalue-normalization/transformvalue-normalization.tentative.html
+++ b/css/css-typed-om/stylevalue-normalization/transformvalue-normalization.tentative.html
@@ -124,18 +124,23 @@
     desc: 'skew() with only X'
   },
   {
+    cssText: 'skew(90deg, 0deg)',
+    expected: new CSSSkew(CSS.deg(90), CSS.deg(0)),
+    desc: 'skew() with X and Y which is 0 value'
+  },
+  {
     cssText: 'skew(90deg, 45deg)',
     expected: new CSSSkew(CSS.deg(90), CSS.deg(45)),
     desc: 'skew() with X and Y'
   },
   {
     cssText: 'skewX(90deg)',
-    expected: new CSSSkew(CSS.deg(90), CSS.deg(0)),
+    expected: new CSSSkewX(CSS.deg(90)),
     desc: 'skewX()'
   },
   {
     cssText: 'skewY(90deg)',
-    expected: new CSSSkew(CSS.deg(0), CSS.deg(90)),
+    expected: new CSSSkewY(CSS.deg(90)),
     desc: 'skewY()'
   },
   {
@@ -153,12 +158,14 @@
 
 test(t => {
   test_transform_normalization(t,
-      'translate(1px) rotateX(90deg) perspective(1px) skew(90deg) scale3d(1, 2, 3)',
+      'translate(1px) rotateX(90deg) perspective(1px) skew(90deg) skewX(20deg) skewY(30deg) scale3d(1, 2, 3)',
       new CSSTransformValue([
         new CSSTranslate(CSS.px(1), CSS.px(0)),
         new CSSRotate(CSS.number(1), CSS.number(0), CSS.number(0), CSS.deg(90)),
         new CSSPerspective(CSS.px(1)),
         new CSSSkew(CSS.deg(90), CSS.deg(0)),
+        new CSSSkewX(CSS.deg(20)),
+        new CSSSkewY(CSS.deg(30)),
         new CSSScale(CSS.number(1), CSS.number(2), CSS.number(3)),
       ]));
 }, 'Normalizing a <transform-list> returns a CSSTransformValue containing all the transforms');
diff --git a/css/css-typed-om/stylevalue-serialization/cssTransformValue.tentative.html b/css/css-typed-om/stylevalue-serialization/cssTransformValue.tentative.html
index 00d723f..dc87e81 100644
--- a/css/css-typed-om/stylevalue-serialization/cssTransformValue.tentative.html
+++ b/css/css-typed-om/stylevalue-serialization/cssTransformValue.tentative.html
@@ -50,6 +50,16 @@
     desc: 'CSSSkew with Y which is 0 value'
   },
   {
+    value: new CSSSkewX(CSS.deg(90)),
+    cssText: 'skewX(90deg)',
+    desc: 'CSSSkewX'
+  },
+  {
+    value: new CSSSkewY(CSS.deg(90)),
+    cssText: 'skewY(90deg)',
+    desc: 'CSSSkewY'
+  },
+  {
     value: new CSSPerspective(CSS.px(1)),
     cssText: 'perspective(1px)',
     desc: 'CSSPerspective'
diff --git a/css/css-typed-om/stylevalue-serialization/cssUnparsedValue.html b/css/css-typed-om/stylevalue-serialization/cssUnparsedValue.html
index 8b7a868..6fc2e02 100644
--- a/css/css-typed-om/stylevalue-serialization/cssUnparsedValue.html
+++ b/css/css-typed-om/stylevalue-serialization/cssUnparsedValue.html
@@ -11,25 +11,25 @@
 'use strict';
 
 test(() => {
-  assert_equals(new CSSUnparsedValue('lem', 'on', 'ade').toString(), 'lemonade');
+  assert_equals(new CSSUnparsedValue(['lem', 'on', 'ade']).toString(), 'lemonade');
 }, 'CSSUnparsedValue containing strings serializes to its concatenated contents');
 
 test(() => {
-  assert_equals(new CSSUnparsedValue(
+  assert_equals(new CSSUnparsedValue([
       new CSSVariableReferenceValue('--A',
-        new CSSUnparsedValue(new CSSVariableReferenceValue('--B'))),
-      new CSSVariableReferenceValue('--C')).toString(),
+        new CSSUnparsedValue([new CSSVariableReferenceValue('--B')])),
+      new CSSVariableReferenceValue('--C')]).toString(),
     'var(--A,var(--B))var(--C)');
 }, 'CSSUnparsedValue containing variable references serializes its ' +
    'concatenated contents');
 
 test(() => {
-  assert_equals(new CSSUnparsedValue('foo', 'bar ',
+  assert_equals(new CSSUnparsedValue(['foo', 'bar ',
       new CSSVariableReferenceValue('--A',
-        new CSSUnparsedValue('baz ',
-          new CSSVariableReferenceValue('--B'), 'lemon')),
+        new CSSUnparsedValue(['baz ',
+          new CSSVariableReferenceValue('--B'), 'lemon'])),
       new CSSVariableReferenceValue('--C',
-        new CSSUnparsedValue('ade'))).toString(),
+        new CSSUnparsedValue(['ade']))]).toString(),
     'foobar var(--A,baz var(--B)lemon)var(--C,ade)');
 }, 'CSSUnparsedValue containing mix of strings and variable references ' +
    'serializes to its concatenated contents');
diff --git a/css/css-typed-om/stylevalue-subclasses/cssSkewX.tentative.html b/css/css-typed-om/stylevalue-subclasses/cssSkewX.tentative.html
new file mode 100644
index 0000000..1e61414
--- /dev/null
+++ b/css/css-typed-om/stylevalue-subclasses/cssSkewX.tentative.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSSkewX tests</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#cssskewx">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<script>
+'use strict';
+
+const gInvalidTestCases = [
+  { value: 'auto', desc: 'a keyword'},
+  { value: 3.14, desc: 'a double'},
+  { value: 0, desc: 'a unitless zero'},
+  { value: '10deg', desc: 'a string angle'},
+  { value: CSS.number(10), desc: 'a number CSSUnitValue'},
+  { value: CSS.s(10), desc: 'a time dimension CSSUnitValue'},
+  { value: new CSSMathSum(CSS.px(1)), desc: 'a CSSMathValue of length type' },
+];
+
+for (const {value, desc} of gInvalidTestCases) {
+  test(() => {
+    assert_throws(new TypeError(), () => new CSSSkewX(value));
+  }, 'Constructing a CSSSkewX with ' + desc + ' throws a TypeError');
+}
+
+for (const {value, desc} of gInvalidTestCases) {
+  test(() => {
+    let skewX = new CSSSkewX(CSS.deg(0));
+    assert_throws(new TypeError(), () => skewX.ax = value);
+    assert_style_value_equals(skewX.ax, CSS.deg(0));
+  }, 'Updating CSSSkewX.ax with ' + desc + ' throws a TypeError');
+}
+
+const gValidTestCases = [
+  { value: CSS.deg(-3.14), desc: 'an angle CSSUnitValue' },
+  { value: new CSSMathSum(CSS.deg(1)), desc: 'a CSSMathValue of angle type' },
+];
+
+for (const {value: ax, desc: axDesc} of gValidTestCases) {
+  test(() => {
+    const skewX = new CSSSkewX(ax);
+    assert_equals(skewX.ax, ax);
+    assert_true(skewX.is2D);
+  }, 'CSSSkewX can be constructed from ' + axDesc);
+}
+
+for (const {value, desc} of gValidTestCases) {
+  test(() => {
+    let skewX = new CSSSkewX(CSS.deg(0));
+    skewX.ax = value;
+    assert_style_value_equals(skewX.ax, value);
+  }, 'CSSSkew.ax can be updated to ' + desc);
+}
+
+test(() => {
+  let skewX = new CSSSkewX(CSS.deg(0));
+  skewX.is2D = false;
+  assert_true(skewX.is2D);
+}, 'Modifying skewX.is2D is a no-op');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssSkewY.tentative.html b/css/css-typed-om/stylevalue-subclasses/cssSkewY.tentative.html
new file mode 100644
index 0000000..643d2f6
--- /dev/null
+++ b/css/css-typed-om/stylevalue-subclasses/cssSkewY.tentative.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSSkewY tests</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#cssskewy">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<script>
+'use strict';
+
+const gInvalidTestCases = [
+  { value: 'auto', desc: 'a keyword'},
+  { value: 3.14, desc: 'a double'},
+  { value: 0, desc: 'a unitless zero'},
+  { value: '10deg', desc: 'a string angle'},
+  { value: CSS.number(10), desc: 'a number CSSUnitValue'},
+  { value: CSS.s(10), desc: 'a time dimension CSSUnitValue'},
+  { value: new CSSMathSum(CSS.px(1)), desc: 'a CSSMathValue of length type' },
+];
+
+for (const {value, desc} of gInvalidTestCases) {
+  test(() => {
+    assert_throws(new TypeError(), () => new CSSSkewY(value));
+  }, 'Constructing a CSSSkewY with ' + desc + ' throws a TypeError');
+}
+
+for (const {value, desc} of gInvalidTestCases) {
+  test(() => {
+    let skewY = new CSSSkewY(CSS.deg(0));
+    assert_throws(new TypeError(), () => skewY.ay = value);
+    assert_style_value_equals(skewY.ay, CSS.deg(0));
+  }, 'Updating CSSSkewY.ay with ' + desc + ' throws a TypeError');
+}
+
+const gValidTestCases = [
+  { value: CSS.deg(-3.14), desc: 'an angle CSSUnitValue' },
+  { value: new CSSMathSum(CSS.deg(1)), desc: 'a CSSMathValue of angle type' },
+];
+
+for (const {value: ay, desc: ayDesc} of gValidTestCases) {
+  test(() => {
+    const skewY = new CSSSkewY(ay);
+    assert_equals(skewY.ay, ay);
+    assert_true(skewY.is2D);
+  }, 'CSSSkewY can be constructed from ' + ayDesc);
+}
+
+for (const {value, desc} of gValidTestCases) {
+  test(() => {
+    let skewY = new CSSSkewY(CSS.deg(0));
+    skewY.ay = value;
+    assert_style_value_equals(skewY.ay, value);
+  }, 'CSSSkewY.ay can be updated to ' + desc);
+}
+
+test(() => {
+  let skewY = new CSSSkewY(CSS.deg(0));
+  skewY.is2D = false;
+  assert_true(skewY.is2D);
+}, 'Modifying skewY.is2D is a no-op');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix-relative-units.html b/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix-relative-units.html
new file mode 100644
index 0000000..67b63a6
--- /dev/null
+++ b/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix-relative-units.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSTransformComponent.toMatrix with Relative Units</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix">
+<meta name="assert" content="Test CSSTransformComponent.toMatrix throws when given relative units" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<script>
+'use strict';
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    return new CSSTranslate(
+      new CSSUnitValue(1, 'px'),
+      new CSSUnitValue(1, 'em')
+    ).toMatrix();
+  });
+}, 'CSSTranslate.toMatrix() containing relative units throws TypeError');
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    return new CSSPerspective(new CSSUnitValue(1, 'em')).toMatrix();
+  });
+}, 'CSSPerspective.toMatrix() containing relative units throws TypeError');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix.html b/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix.html
new file mode 100644
index 0000000..14d819b
--- /dev/null
+++ b/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSTransformComponent.toMatrix tests</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix">
+<meta name="assert" content="Test CSSTransformComponent.toMatrix for each type of component" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<script>
+'use strict';
+
+const gEpsilon = 1e-6;
+
+test(() => {
+  const component = new CSSTranslate(
+    new CSSUnitValue(1, 'px'),
+    new CSSUnitValue(2, 'px'),
+    new CSSUnitValue(3, 'px')
+  );
+  const expectedMatrix = (new DOMMatrixReadOnly()).translate(1, 2, 3);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, 1e-8);
+}, 'CSSTranslate.toMatrix() returns correct matrix');
+
+test(() => {
+  const component = new CSSRotate(
+    new CSSUnitValue(1, 'number'),
+    new CSSUnitValue(2, 'number'),
+    new CSSUnitValue(3, 'number'),
+    new CSSUnitValue(90, 'deg')
+  );
+  const expectedMatrix = (new DOMMatrixReadOnly()).rotateAxisAngle(1, 2, 3, 90);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSRotate.toMatrix() returns correct matrix');
+
+test(() => {
+  const component = new CSSScale(1, 2, 3);
+  const expectedMatrix = (new DOMMatrixReadOnly()).scale(1, 2, 3);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSScale.toMatrix() returns correct matrix');
+
+test(() => {
+  const alpha = 10, beta = 20;
+  const component = new CSSSkew(
+    new CSSUnitValue(alpha, 'rad'),
+    new CSSUnitValue(beta, 'rad')
+  );
+  const expectedMatrix = new DOMMatrixReadOnly(
+        [1, Math.tan(beta), 0, 0, Math.tan(alpha), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSSkew.toMatrix() returns correct matrix');
+
+test(() => {
+  const component = new CSSSkewX(
+    new CSSUnitValue(10, 'rad'),
+  );
+  const expectedMatrix = (new DOMMatrixReadOnly()).skewX(10);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSSkewX.toMatrix() returns correct matrix');
+
+test(() => {
+  const component = new CSSSkewY(
+    new CSSUnitValue(10, 'rad'),
+  );
+  const expectedMatrix = (new DOMMatrixReadOnly()).skewY(10);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSSkewY.toMatrix() returns correct matrix');
+
+test(() => {
+  const length = 10;
+  const component = new CSSPerspective(new CSSUnitValue(length, 'px'));
+  const expectedMatrix = new DOMMatrixReadOnly(
+        [1, 0, 0, 0,
+         0, 1, 0, 0,
+         0, 0, 1, -1/length,
+         0, 0, 0, 1]);
+  assert_matrix_approx_equals(component.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSPerspective.toMatrix() returns correct matrix');
+
+test(() => {
+  const matrix = new DOMMatrixReadOnly(
+          [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+  const component = new CSSMatrixComponent(matrix);
+  assert_matrix_approx_equals(component.toMatrix(), matrix, gEpsilon);
+}, 'CSSMatrixComponent.toMatrix() returns correct matrix');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssTransformValue-toMatrix.html b/css/css-typed-om/stylevalue-subclasses/cssTransformValue-toMatrix.html
new file mode 100644
index 0000000..c0e0bbc
--- /dev/null
+++ b/css/css-typed-om/stylevalue-subclasses/cssTransformValue-toMatrix.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSTransformValue.toMatrix</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-csstransformcomponent-tomatrix">
+<meta name="assert" content="Test CSSTransformValue.toMatrix multiplies component matrices" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<script>
+'use strict';
+
+const gEpsilon = 1e-6;
+
+test(() => {
+  const transformMatrix = new DOMMatrixReadOnly([1, 1, 1, 1, 1, 1]);
+  const transformArray = [
+    new CSSScale(2, 2),
+    new CSSMatrixComponent(transformMatrix),
+    new CSSScale(5, 6)
+  ];
+
+  let expectedMatrix = new DOMMatrix();
+  expectedMatrix.scaleSelf(2, 2);
+  expectedMatrix.multiplySelf(transformMatrix);
+  expectedMatrix.scaleSelf(5, 6);
+
+  const transform = new CSSTransformValue(transformArray);
+  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSTransformValue.toMatrix() multiplies its component matrices');
+
+test(() => {
+  const transformMatrix = new DOMMatrixReadOnly([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+  let transformArray = [
+    new CSSTranslate(CSS.px(1), CSS.px(1), CSS.px(1)),
+    new CSSRotate(1, 2, 3, CSS.deg(90)),
+    new CSSScale(2, 3, 2),
+    new CSSMatrixComponent(transformMatrix),
+  ];
+
+  transformArray.forEach(transform => transform.is2D = true);
+
+  let expectedMatrix = new DOMMatrix();
+  expectedMatrix.translateSelf(1, 1);
+  expectedMatrix.rotateSelf(90);
+  expectedMatrix.scaleSelf(2, 3);
+  expectedMatrix.multiplySelf(new DOMMatrixReadOnly([1, 2, 5, 6, 13, 14]));
+
+  const transform = new CSSTransformValue(transformArray);
+  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, gEpsilon);
+}, 'CSSTransformValue.toMatrix() respects is2D changes in its components');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssTransformValue.tentative.html b/css/css-typed-om/stylevalue-subclasses/cssTransformValue.tentative.html
index b16d736..bc1ace6 100644
--- a/css/css-typed-om/stylevalue-subclasses/cssTransformValue.tentative.html
+++ b/css/css-typed-om/stylevalue-subclasses/cssTransformValue.tentative.html
@@ -9,8 +9,6 @@
 <script>
 'use strict';
 
-const EPSILON = 1e-6;
-
 test(() => {
   assert_throws(new TypeError(), () => new CSSTransformValue());
   assert_throws(new TypeError(), () => new CSSTransformValue([]));
@@ -52,106 +50,6 @@
 }, 'CSSTransformValue.is2D is readonly');
 
 test(() => {
-  assert_throws(new TypeError(), () => new CSSTransformValue([new CSSTranslate(CSS.px(1), CSS.em(1))]).toMatrix());
-  assert_throws(new TypeError(), () => new CSSTransformValue([new CSSPerspective(CSS.em(1))]).toMatrix());
-}, 'Calling CSSTransformValue.toMatrix containing relative units throws TypeError');
-
-test(() => {
-  const transform = new CSSTransformValue([
-    new CSSTranslate(CSS.px(1), CSS.px(2), CSS.px(3))
-  ]);
-  const expectedMatrix = (new DOMMatrixReadOnly()).translate(1, 2, 3);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, 1e-8);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSTranslate');
-
-test(() => {
-  const transform = new CSSTransformValue([
-    new CSSRotate(CSS.number(1), CSS.number(2), CSS.number(3), CSS.deg(90))
-  ]);
-  const expectedMatrix = (new DOMMatrixReadOnly()).rotateAxisAngle(1, 2, 3, 90);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSRotate');
-
-test(() => {
-  const transform = new CSSTransformValue([
-      new CSSScale(CSS.number(1), CSS.number(2), CSS.number(3))
-  ]);
-  const expectedMatrix = (new DOMMatrixReadOnly()).scale(1, 2, 3);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSScale');
-
-test(() => {
-  const alpha = 10;
-  const beta = 20;
-  const transform = new CSSTransformValue([
-      new CSSSkew(CSS.rad(alpha), CSS.rad(beta))
-  ]);
-  const expectedMatrix = new DOMMatrixReadOnly(
-        [1, Math.tan(beta), 0, 0, Math.tan(alpha), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSSkew');
-
-test(() => {
-  const length = 10;
-  const transform = new CSSTransformValue([
-      new CSSPerspective(CSS.px(length))
-  ]);
-  const expectedMatrix = new DOMMatrixReadOnly(
-        [1, 0, 0, 0,
-         0, 1, 0, 0,
-         0, 0, 1, -1/length,
-         0, 0, 0, 1]);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSPerspective');
-
-test(() => {
-  const matrix = new DOMMatrixReadOnly(
-          [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
-  const transform = new CSSTransformValue([
-      new CSSMatrixComponent(matrix)
-  ]);
-  assert_matrix_approx_equals(transform.toMatrix(), matrix, EPSILON);
-}, 'CSSTransformValue.toMatrix returns correct matrix for CSSMatrixComponent');
-
-test(() => {
-  const transformMatrix = new DOMMatrixReadOnly([1, 1, 1, 1, 1, 1]);
-  const transformArray = [
-    new CSSScale(2, 2),
-    new CSSMatrixComponent(transformMatrix),
-    new CSSScale(5, 6)
-  ];
-
-  let expectedMatrix = new DOMMatrix();
-  expectedMatrix.scaleSelf(2, 2);
-  expectedMatrix.multiplySelf(transformMatrix);
-  expectedMatrix.scaleSelf(5, 6);
-
-  const transform = new CSSTransformValue(transformArray);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix multiplies its component matrices');
-
-test(() => {
-  const transformMatrix = new DOMMatrixReadOnly([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
-  let transformArray = [
-    new CSSTranslate(CSS.px(1), CSS.px(1), CSS.px(1)),
-    new CSSRotate(1, 2, 3, CSS.deg(90)),
-    new CSSScale(2, 3, 2),
-    new CSSMatrixComponent(transformMatrix),
-  ];
-
-  transformArray.forEach(transform => transform.is2D = true);
-
-  let expectedMatrix = new DOMMatrix();
-  expectedMatrix.translateSelf(1, 1);
-  expectedMatrix.rotateSelf(90);
-  expectedMatrix.scaleSelf(2, 3);
-  expectedMatrix.multiplySelf(new DOMMatrixReadOnly([1, 2, 5, 6, 13, 14]));
-
-  const transform = new CSSTransformValue(transformArray);
-  assert_matrix_approx_equals(transform.toMatrix(), expectedMatrix, EPSILON);
-}, 'CSSTransformValue.toMatrix respects is2D changes in its components');
-
-test(() => {
   const transformArray = [
     new CSSScale(2, 2),
     new CSSMatrixComponent(new DOMMatrixReadOnly([1, 1, 1, 1, 1, 1])),
diff --git a/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue.html b/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue.html
index 3a994dd..8af27e1 100644
--- a/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue.html
+++ b/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue.html
@@ -37,7 +37,7 @@
 
 for (const {args, description} of gTestArguments) {
   test(() => {
-    const result = new CSSUnparsedValue(...args);
+    const result = new CSSUnparsedValue(args);
 
     assert_not_equals(result, null,
         'A CSSUnparsedValue should be created');
diff --git a/css/css-typed-om/stylevalue-subclasses/cssVariableReferenceValue.html b/css/css-typed-om/stylevalue-subclasses/cssVariableReferenceValue.html
index 24082eb..e729acf 100644
--- a/css/css-typed-om/stylevalue-subclasses/cssVariableReferenceValue.html
+++ b/css/css-typed-om/stylevalue-subclasses/cssVariableReferenceValue.html
@@ -22,7 +22,7 @@
 
 test(() => {
   const result = new CSSVariableReferenceValue('--foo',
-      new CSSUnparsedValue('lemon'));
+      new CSSUnparsedValue(['lemon']));
 
   assert_not_equals(result, null,
       'A CSSVariableReferenceValue should be created');
@@ -30,7 +30,7 @@
       'Variable member should be same as passed in the constructor');
   assert_not_equals(result.fallback, null,
       'Fallback member should not be null');
-  assert_style_value_equals(result.fallback, new CSSUnparsedValue('lemon'),
+  assert_style_value_equals(result.fallback, new CSSUnparsedValue(['lemon']),
       'Fallback member should be as same as passed in the constructor');
 }, 'CSSVariableReferenceValue can be constructed with fallback');
 
diff --git a/css/css-typed-om/the-stylepropertymap/computed/computed.tentative.html b/css/css-typed-om/the-stylepropertymap/computed/computed.tentative.html
index d2a68e7..edadefa 100644
--- a/css/css-typed-om/the-stylepropertymap/computed/computed.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/computed/computed.tentative.html
@@ -38,7 +38,7 @@
 
 test(() => {
   const result = styleMap.get('--foo');
-  assert_style_value_equals(result, new CSSUnparsedValue(' auto'));
+  assert_style_value_equals(result, new CSSUnparsedValue([' auto']));
 }, 'Computed StylePropertyMap contains custom property declarations in style rules');
 
 test(() => {
@@ -48,7 +48,7 @@
 
 test(() => {
   const result = styleMap.get('--bar');
-  assert_style_value_equals(result, new CSSUnparsedValue(' 5'));
+  assert_style_value_equals(result, new CSSUnparsedValue([' 5']));
 }, 'Computed StylePropertyMap contains custom property declarations in inline rules');
 
 test(() => {
diff --git a/css/css-typed-om/the-stylepropertymap/computed/get.html b/css/css-typed-om/the-stylepropertymap/computed/get.html
index c11ca73..3e061e1 100644
--- a/css/css-typed-om/the-stylepropertymap/computed/get.html
+++ b/css/css-typed-om/the-stylepropertymap/computed/get.html
@@ -24,7 +24,7 @@
 test(t => {
   const styleMap = createComputedStyleMap(t, '--foo: auto; --bar: 10px');
   assert_style_value_equals(styleMap.get('--foo'),
-      new CSSUnparsedValue(' auto'));
+      new CSSUnparsedValue([' auto']));
 }, 'Getting a valid custom property from computed style returns the ' +
    'correct entry');
 
diff --git a/css/css-typed-om/the-stylepropertymap/computed/getAll.tentative.html b/css/css-typed-om/the-stylepropertymap/computed/getAll.tentative.html
index cd8dcd0..ca7d3b6 100644
--- a/css/css-typed-om/the-stylepropertymap/computed/getAll.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/computed/getAll.tentative.html
@@ -31,7 +31,7 @@
 
 test(t => {
   const styleMap = createComputedStyleMap(t, '--foo: auto; --bar: 10px');
-  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue(' auto')]);
+  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue([' auto'])]);
 }, 'Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/computed/iterable.tentative.html b/css/css-typed-om/the-stylepropertymap/computed/iterable.tentative.html
index 07d59d8..4006b20 100644
--- a/css/css-typed-om/the-stylepropertymap/computed/iterable.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/computed/iterable.tentative.html
@@ -34,9 +34,9 @@
 
 test(t => {
   const styleMap = createComputedStyleMap(t, '--A: A; --C: C; color: red; --B: B;');
-  assert_style_value_equals(findInStyleMap(styleMap, '--A'), new CSSUnparsedValue(' A'));
-  assert_style_value_equals(findInStyleMap(styleMap, '--B'), new CSSUnparsedValue(' B'));
-  assert_style_value_equals(findInStyleMap(styleMap, '--C'), new CSSUnparsedValue(' C'));
+  assert_style_value_equals(findInStyleMap(styleMap, '--A'), new CSSUnparsedValue([' A']));
+  assert_style_value_equals(findInStyleMap(styleMap, '--B'), new CSSUnparsedValue([' B']));
+  assert_style_value_equals(findInStyleMap(styleMap, '--C'), new CSSUnparsedValue([' C']));
 }, 'StylePropertyMap iterator returns custom properties with the correct CSSStyleValue');
 
 </script>
diff --git a/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.html b/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.html
index 355a509..b1d7ce6 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.html
@@ -45,7 +45,7 @@
 }, 'Declared StylePropertyMap does not contain inline styles');
 
 test(() => {
-  assert_style_value_equals(styleMap.get('--foo'), new CSSUnparsedValue(' auto'));
+  assert_style_value_equals(styleMap.get('--foo'), new CSSUnparsedValue([' auto']));
 }, 'Declared StylePropertyMap contains custom property declarations');
 
 test(() => {
diff --git a/css/css-typed-om/the-stylepropertymap/declared/get.html b/css/css-typed-om/the-stylepropertymap/declared/get.html
index aa649df..c4215cb 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/get.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/get.html
@@ -29,7 +29,7 @@
 test(t => {
   const styleMap = createDeclaredStyleMap(t, '--foo: auto; --bar: 10px');
   assert_style_value_equals(styleMap.get('--foo'),
-      new CSSUnparsedValue(' auto'));
+      new CSSUnparsedValue([' auto']));
 }, 'Getting a valid custom property from CSS rule returns the ' +
    'correct entry');
 
diff --git a/css/css-typed-om/the-stylepropertymap/declared/getAll.tentative.html b/css/css-typed-om/the-stylepropertymap/declared/getAll.tentative.html
index 5fe635c..dd4e90b 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/getAll.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/getAll.tentative.html
@@ -36,7 +36,7 @@
 
 test(t => {
   const styleMap = createDeclaredStyleMap(t, '--foo: auto; --bar: 10px');
-  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue(' auto')]);
+  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue([' auto'])]);
 }, 'Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/declared/iterable.tentative.html b/css/css-typed-om/the-stylepropertymap/declared/iterable.tentative.html
index be61e25..0609010 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/iterable.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/iterable.tentative.html
@@ -42,9 +42,9 @@
 
   assert_array_equals(keys, ['--A', '--B', '--C']);
   assert_style_value_array_equals(values, [
-    new CSSUnparsedValue(' A'),
-    new CSSUnparsedValue(' B'),
-    new CSSUnparsedValue(' C'),
+    new CSSUnparsedValue([' A']),
+    new CSSUnparsedValue([' B']),
+    new CSSUnparsedValue([' C']),
   ])
 }, 'StylePropertyMap iterator returns custom properties with the correct CSSStyleValue');
 
diff --git a/css/css-typed-om/the-stylepropertymap/declared/set.tentative.html b/css/css-typed-om/the-stylepropertymap/declared/set.tentative.html
index fe4cced..232c7d3 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/set.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/set.tentative.html
@@ -63,11 +63,11 @@
 test(t => {
   let styleMap = createDeclaredStyleMap(t, '');
 
-  styleMap.set('--foo', new CSSUnparsedValue('auto'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('auto'));
+  styleMap.set('--foo', new CSSUnparsedValue(['auto']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['auto']));
 
   styleMap.set('--foo', '20px');
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('20px'));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['20px']));
 }, 'Setting a custom property with CSSStyleValue or String updates its value');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/declared/update.tentative.html b/css/css-typed-om/the-stylepropertymap/declared/update.tentative.html
index a5ee95e..8602e2c 100644
--- a/css/css-typed-om/the-stylepropertymap/declared/update.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/declared/update.tentative.html
@@ -46,11 +46,11 @@
 test(t => {
   let styleMap = createDeclaredStyleMap(t, '');
 
-  styleMap.update('--foo', () => new CSSUnparsedValue('auto'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('auto'));
+  styleMap.update('--foo', () => new CSSUnparsedValue(['auto']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['auto']));
 
-  styleMap.update('--foo', () => new CSSUnparsedValue('20px'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('20px'));
+  styleMap.update('--foo', () => new CSSUnparsedValue(['20px']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['20px']));
 }, 'Updating a custom property with CSSStyleValue updates its value');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/inline/get.html b/css/css-typed-om/the-stylepropertymap/inline/get.html
index ac7c6e4..8588e6e 100644
--- a/css/css-typed-om/the-stylepropertymap/inline/get.html
+++ b/css/css-typed-om/the-stylepropertymap/inline/get.html
@@ -29,7 +29,7 @@
 test(t => {
   const styleMap = createInlineStyleMap(t, '--foo: auto; --bar: 10px');
   assert_style_value_equals(styleMap.get('--foo'),
-      new CSSUnparsedValue(' auto'));
+      new CSSUnparsedValue([' auto']));
 }, 'Getting a valid custom property from inline style returns the ' +
    'correct entry');
 
diff --git a/css/css-typed-om/the-stylepropertymap/inline/getAll.tentative.html b/css/css-typed-om/the-stylepropertymap/inline/getAll.tentative.html
index 62cbec3..c7e3700 100644
--- a/css/css-typed-om/the-stylepropertymap/inline/getAll.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/inline/getAll.tentative.html
@@ -36,7 +36,7 @@
 
 test(t => {
   const styleMap = createInlineStyleMap(t, '--foo: auto; --bar: 10px');
-  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue(' auto')]);
+  assert_style_value_array_equals(styleMap.getAll('--foo'), [new CSSUnparsedValue([' auto'])]);
 }, 'Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/inline/iterable.tentative.html b/css/css-typed-om/the-stylepropertymap/inline/iterable.tentative.html
index cbf76d8..ebd1df7 100644
--- a/css/css-typed-om/the-stylepropertymap/inline/iterable.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/inline/iterable.tentative.html
@@ -42,9 +42,9 @@
 
   assert_array_equals(keys, ['--A', '--B', '--C']);
   assert_style_value_array_equals(values, [
-    new CSSUnparsedValue(' A'),
-    new CSSUnparsedValue(' B'),
-    new CSSUnparsedValue(' C'),
+    new CSSUnparsedValue([' A']),
+    new CSSUnparsedValue([' B']),
+    new CSSUnparsedValue([' C']),
   ])
 }, 'StylePropertyMap iterator returns custom properties with the correct CSSStyleValue');
 
diff --git a/css/css-typed-om/the-stylepropertymap/inline/set.tentative.html b/css/css-typed-om/the-stylepropertymap/inline/set.tentative.html
index dc8ccc5..67d2426d 100644
--- a/css/css-typed-om/the-stylepropertymap/inline/set.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/inline/set.tentative.html
@@ -63,11 +63,11 @@
 test(t => {
   let styleMap = createInlineStyleMap(t, '');
 
-  styleMap.set('--foo', new CSSUnparsedValue('auto'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('auto'));
+  styleMap.set('--foo', new CSSUnparsedValue(['auto']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['auto']));
 
   styleMap.set('--foo', '20px');
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('20px'));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['20px']));
 }, 'Setting a custom property with CSSStyleValue or String updates its value');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/inline/update.tentative.html b/css/css-typed-om/the-stylepropertymap/inline/update.tentative.html
index ac2d834..77f2c10 100644
--- a/css/css-typed-om/the-stylepropertymap/inline/update.tentative.html
+++ b/css/css-typed-om/the-stylepropertymap/inline/update.tentative.html
@@ -46,11 +46,11 @@
 test(t => {
   let styleMap = createInlineStyleMap(t, '');
 
-  styleMap.update('--foo', () => new CSSUnparsedValue('auto'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('auto'));
+  styleMap.update('--foo', () => new CSSUnparsedValue(['auto']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['auto']));
 
-  styleMap.update('--foo', () => new CSSUnparsedValue('20px'));
-  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue('20px'));
+  styleMap.update('--foo', () => new CSSUnparsedValue(['20px']));
+  assert_style_value_array_equals(styleMap.get('--foo'), new CSSUnparsedValue(['20px']));
 }, 'Updating a custom property with CSSStyleValue updates its value');
 
 test(t => {
diff --git a/css/css-typed-om/the-stylepropertymap/properties/animation-direction.html b/css/css-typed-om/the-stylepropertymap/properties/animation-direction.html
new file mode 100644
index 0000000..8ab0363
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/animation-direction.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'animation-direction' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+// FIXME: animation-direction is list-valued. Run list-valued tests here too.
+runPropertyTests('animation-direction', [
+  { syntax: 'normal' },
+  { syntax: 'reverse' },
+  { syntax: 'alternate-reverse' },
+  { syntax: 'alternate' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/background-image.html b/css/css-typed-om/the-stylepropertymap/properties/background-image.html
new file mode 100644
index 0000000..d584a0f
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/background-image.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'background-image' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+// FIXME: background-image is list-valued. Run list-valued tests here too.
+runPropertyTests('background-image', [
+  { syntax: 'none' },
+  { syntax: '<image>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/border-style.html b/css/css-typed-om/the-stylepropertymap/properties/border-style.html
new file mode 100644
index 0000000..1328ab1
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/border-style.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>border style properties</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+for (const suffix of ['top', 'left', 'right', 'bottom']) {
+  runPropertyTests('border-' + suffix + '-style', [
+    { syntax: 'none' },
+    { syntax: 'solid' },
+    // and other keywords
+  ]);
+}
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/border-width.html b/css/css-typed-om/the-stylepropertymap/properties/border-width.html
new file mode 100644
index 0000000..d1fe0b0
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/border-width.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>border width properties</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+function assert_is_zero_px(result) {
+  assert_style_value_equals(result, new CSSUnitValue(0, 'px'));
+}
+
+for (const suffix of ['top', 'left', 'right', 'bottom']) {
+  runPropertyTests('border-' + suffix + '-width', [
+    // Computed value is 0 when border-style is 'none'.
+    // FIXME: Add separate test where border-style is not 'none' or 'hidden'.
+    { syntax: 'thin', computed: assert_is_zero_px },
+    { syntax: 'medium', computed: assert_is_zero_px },
+    { syntax: 'thick', computed: assert_is_zero_px },
+    { syntax: '<length>' },
+  ]);
+}
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/bottom.html b/css/css-typed-om/the-stylepropertymap/properties/bottom.html
new file mode 100644
index 0000000..36fe514
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/bottom.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'bottom' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('bottom', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/display.html b/css/css-typed-om/the-stylepropertymap/properties/display.html
new file mode 100644
index 0000000..51f3532
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/display.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'display' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('display', [
+  { syntax: 'none' },
+  { syntax: 'block' },
+  // and other keywords
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/height.html b/css/css-typed-om/the-stylepropertymap/properties/height.html
new file mode 100644
index 0000000..7d126ec
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/height.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'height' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('height', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/left.html b/css/css-typed-om/the-stylepropertymap/properties/left.html
new file mode 100644
index 0000000..6e82d2e
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/left.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'left' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('left', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/margin.html b/css/css-typed-om/the-stylepropertymap/properties/margin.html
new file mode 100644
index 0000000..679fb4a
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/margin.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>margin properties</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+for (const suffix of ['top', 'left', 'right', 'bottom']) {
+  runPropertyTests('margin-' + suffix, [
+    {
+      syntax: 'auto',
+      // Depending on which CSS spec is implemented, the computed value
+      // can be 'auto' or a browser specific value.
+      // FIXME: Figure out how to test this.
+      computed: () => {}
+    },
+    { syntax: '<percentage>' },
+    { syntax: '<length>' },
+  ]);
+}
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/object-position.html b/css/css-typed-om/the-stylepropertymap/properties/object-position.html
new file mode 100644
index 0000000..0dd30e9
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/object-position.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'object-position' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('object-position', [
+  { syntax: '<position>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/padding.html b/css/css-typed-om/the-stylepropertymap/properties/padding.html
new file mode 100644
index 0000000..c740ae7
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/padding.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>padding properties</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+for (const suffix of ['top', 'left', 'right', 'bottom']) {
+  runPropertyTests('padding-' + suffix, [
+    { syntax: '<percentage>' },
+    { syntax: '<length>' },
+  ]);
+}
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/position.html b/css/css-typed-om/the-stylepropertymap/properties/position.html
new file mode 100644
index 0000000..4b36ec7
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/position.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'position' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('position', [
+  { syntax: 'relative' },
+  { syntax: 'absolute' },
+  // and other keywords
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js b/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
new file mode 100644
index 0000000..f7f3bd8
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
@@ -0,0 +1,208 @@
+const gTestSyntaxExamples = {
+  '<length>': {
+    description: 'a length',
+    examples: [
+      {
+        description: "zero px",
+        input: new CSSUnitValue(0, 'px')
+      },
+      {
+        description: "a negative em",
+        input: new CSSUnitValue(-3.14, 'em'),
+        defaultComputed: value => {
+          // 'ems' are relative units, so just check that it computes to px
+          assert_class_string(value, 'CSSUnitValue',
+            '"em" lengths must compute to a CSSUnitValue');
+          assert_equals(value.unit, 'px', 'unit');
+        }
+      },
+      {
+        description: "a positive cm",
+        input: new CSSUnitValue(3.14, 'cm'),
+        defaultComputed: value => {
+          // 'cms' are relative units, so just check that it computes to px
+          assert_class_string(value, 'CSSUnitValue',
+            '"cm" lengths must compute to a CSSUnitValue');
+          assert_equals(value.unit, 'px', 'unit');
+        }
+      },
+    ],
+  },
+  '<percentage>': {
+    description: 'a percent',
+    examples: [
+      {
+        description: "zero percent",
+        input: new CSSUnitValue(0, 'percent')
+      },
+      {
+        description: "a negative percent",
+        input: new CSSUnitValue(-3.14, 'percent')
+      },
+      {
+        description: "a positive percent",
+        input: new CSSUnitValue(3.14, 'percent')
+      },
+    ],
+  },
+  '<time>': {
+    description: 'a time',
+    examples: [
+      {
+        description: "zero seconds",
+        input: new CSSUnitValue(0, 's')
+      },
+      {
+        description: "negative milliseconds",
+        input: new CSSUnitValue(-3.14, 'ms')
+      },
+      {
+        description: "positive seconds",
+        input: new CSSUnitValue(3.14, 's')
+      },
+    ],
+  },
+  '<position>': {
+    description: 'a position',
+    examples: [
+      {
+        decription: "origin position",
+        input: new CSSPositionValue(new CSSUnitValue(0, 'px'), new CSSUnitValue(0, 'px'))
+      }
+    ],
+  },
+  '<image>': {
+    description: 'an image',
+    examples: [
+      {
+        description: "a PNG image",
+        input: new CSSURLImageValue('/media/1x1.png'),
+        defaultComputed: result => {
+          // URLs compute to absolute URLs
+          assert_true(result instanceof CSSURLImageValue,
+            'Computed value should be a CSSURLImageValue');
+          assert_true(result.url.endsWith('/media/1x1.png'),
+            'Computed value should be an absolute URL');
+        }
+      }
+    ],
+  },
+  '<transform>': {
+    description: 'a transform',
+    examples: [
+      {
+        description: 'a transform containing only a translate',
+        input: new CSSTransformValue([
+          new CSSTranslate(
+            new CSSUnitValue(0, 'px'),
+            new CSSUnitValue(1, 'px'),
+            new CSSUnitValue(2, 'px'),
+          )
+        ]),
+      }
+    ],
+  },
+};
+
+// Test setting a value in a style map and then getting it from the inline and
+// computed styles.
+function testPropertyValid(propertyName, examples, specified, computed, description) {
+  test(t => {
+    let element = createDivWithStyle(t);
+
+    for (const example of examples) {
+      element.attributeStyleMap.set(propertyName, example.input);
+
+      // specified style
+      const specifiedResult = element.attributeStyleMap.get(propertyName);
+      if (specified || example.defaultSpecified) {
+        (specified || example.defaultSpecified)(specifiedResult);
+      } else {
+        assert_not_equals(specifiedResult, null,
+          'Specified value must not be null');
+        assert_true(specifiedResult instanceof CSSStyleValue,
+          'Specified value must be a CSSStyleValue');
+        assert_style_value_equals(specifiedResult, example.input,
+          `Setting ${example.description} and getting its specified value`);
+      }
+
+      // computed style
+      const computedResult = element.computedStyleMap().get(propertyName);
+      if (computed || example.defaultComputed) {
+        (computed || example.defaultComputed)(computedResult);
+      } else {
+        assert_not_equals(computedResult, null,
+          'Computed value must not be null');
+        assert_true(computedResult instanceof CSSStyleValue,
+          'Computed value must be a CSSStyleValue');
+        assert_style_value_equals(computedResult, example.input,
+          `Setting ${example.description} and getting its computed value`);
+      }
+    }
+  }, `Can set '${propertyName}' to ${description}`);
+}
+
+// Test that styleMap.set throws for invalid values
+function testPropertyInvalid(propertyName, examples, description) {
+  test(t => {
+    let styleMap = createInlineStyleMap(t);
+    for (const example of examples) {
+      assert_throws(new TypeError(), () => styleMap.set(propertyName, example.input));
+    }
+  }, `Setting '${propertyName}' to ${description} throws TypeError`);
+}
+
+function createKeywordExample(keyword) {
+  return {
+    description: `the '${keyword}' keyword`,
+    examples: [ { input: new CSSKeywordValue(keyword) } ]
+  };
+}
+
+// Run a battery of StylePropertyMap tests on |propertyName|.
+// Second argument is a list of test cases. A test case has the form:
+//
+// {
+//   syntax: "<length>",
+//   specified: /* a callback */ (optional)
+//   computed: /* a callback */ (optional)
+// }
+//
+// If a callback is passed to |specified|, then the callback will be passed
+// the result of calling get() on the inline style map (specified values).
+// The callback should check if the result is expected using assert_* functions.
+// If no callback is passed, then we assert that the result is the same as
+// the input.
+//
+// Same goes for |computed|, but with the computed style map (computed values).
+function runPropertyTests(propertyName, testCases) {
+  let syntaxTested = new Set();
+
+  for (const testCase of testCases) {
+    // Retrieve test examples for this test case's syntax. If the syntax
+    // looks like a keyword, then create an example on the fly.
+    const syntaxExamples = testCase.syntax.match(/^[a-z\-]+$/) ?
+      createKeywordExample(testCase.syntax) :
+      gTestSyntaxExamples[testCase.syntax];
+
+    if (!syntaxExamples)
+      throw new Error(`'${testCase.syntax}' is not a valid CSS component`);
+
+    testPropertyValid(propertyName,
+      syntaxExamples.examples,
+      testCase.specified,
+      testCase.computed,
+      syntaxExamples.description);
+
+    syntaxTested.add(testCase.syntax);
+  }
+
+  // Also test that styleMap.set rejects invalid CSSStyleValues.
+  for (const [syntax, syntaxExamples] of Object.entries(gTestSyntaxExamples)) {
+    if (!syntaxTested.has(syntax)) {
+      testPropertyInvalid(propertyName,
+        syntaxExamples.examples,
+        syntaxExamples.description);
+    }
+  }
+}
diff --git a/css/css-typed-om/the-stylepropertymap/properties/right.html b/css/css-typed-om/the-stylepropertymap/properties/right.html
new file mode 100644
index 0000000..17488dfb
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/right.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'right' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('right', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/text-align.html b/css/css-typed-om/the-stylepropertymap/properties/text-align.html
new file mode 100644
index 0000000..c3d7f2e
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/text-align.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'text-align' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('text-align', [
+  { syntax: 'center' },
+  { syntax: 'justify' },
+  // and other keywords
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/text-decoration-style.html b/css/css-typed-om/the-stylepropertymap/properties/text-decoration-style.html
new file mode 100644
index 0000000..6bed8b6
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/text-decoration-style.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'text-decoration-style' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('text-decoration-style', [
+  { syntax: 'solid' },
+  { syntax: 'double' },
+  // and other keywords
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/top.html b/css/css-typed-om/the-stylepropertymap/properties/top.html
new file mode 100644
index 0000000..f12a76b
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/top.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'top' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('top', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/transform.html b/css/css-typed-om/the-stylepropertymap/properties/transform.html
new file mode 100644
index 0000000..7a85254
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/transform.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'transform' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('transform', [
+  { syntax: 'none' },
+  { syntax: '<transform>' },
+]);
+
+</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/width.html b/css/css-typed-om/the-stylepropertymap/properties/width.html
new file mode 100644
index 0000000..021a87f
--- /dev/null
+++ b/css/css-typed-om/the-stylepropertymap/properties/width.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'width' property</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+runPropertyTests('width', [
+  { syntax: 'auto' },
+  { syntax: '<percentage>' },
+  { syntax: '<length>' },
+]);
+
+</script>
diff --git a/css/css-ui/reference/text-overflow-027-ref.html b/css/css-ui/reference/text-overflow-027-ref.html
new file mode 100644
index 0000000..8fc664b
--- /dev/null
+++ b/css/css-ui/reference/text-overflow-027-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test Reference</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<style>
+div { font-family: monospace; }
+</style>
+
+<p>The test passes if the following text is visible below: 123456 FE…</p>
+<div>123456 FE…</bdo></div>
diff --git a/css/css-ui/reference/text-overflow-028-ref.html b/css/css-ui/reference/text-overflow-028-ref.html
new file mode 100644
index 0000000..b25ab26
--- /dev/null
+++ b/css/css-ui/reference/text-overflow-028-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test Reference</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<style>
+div { font-family: monospace; }
+</style>
+
+<p>The test passes if the following text is visible below: …56 FEDCBA</p>
+<div>…56 FEDCBA</div>
diff --git a/css/css-ui/reference/text-overflow-029-ref.html b/css/css-ui/reference/text-overflow-029-ref.html
new file mode 100644
index 0000000..9cdd741
--- /dev/null
+++ b/css/css-ui/reference/text-overflow-029-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>CSS Basic User Interface Reference</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<style>
+div { font: 20px monospace; }
+</style>
+
+<div>Test passed…</div>
diff --git a/css/css-ui/text-overflow-027.html b/css/css-ui/text-overflow-027.html
new file mode 100644
index 0000000..fcb0104
--- /dev/null
+++ b/css/css-ui/text-overflow-027.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: text-overflow applies visually to bidi</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
+<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
+<link rel="match" href="reference/text-overflow-027-ref.html">
+<meta name="flags" content="">
+<meta name="assert" content="text-overflow is a visual operation that occurs after layout, and therfore ellides text from the visual end of the line, even in bidi situations">
+<style>
+div {
+  font-family: monospace;
+  width: 10ch;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: pre;
+}
+</style>
+
+<p>The test passes if the following text is visible below: 123456 FE…</p>
+<div>123456 <bdo dir=rtl>ABCDEF</bdo></div>
diff --git a/css/css-ui/text-overflow-028.html b/css/css-ui/text-overflow-028.html
new file mode 100644
index 0000000..af906e1
--- /dev/null
+++ b/css/css-ui/text-overflow-028.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: text-overflow applies visually to bidi</title>
+<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
+<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
+<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
+<link rel="match" href="reference/text-overflow-028-ref.html">
+<meta name="flags" content="">
+<meta name="assert" content="text-overflow is a visual operation that occurs after layout, and therfore ellides text from the visual end of the line, even in bidi situations">
+<style>
+div {
+  font-family: monospace;
+  width: 10ch;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: pre;
+}
+</style>
+
+<p>The test passes if the following text is visible below: …56 FEDCBA</p>
+<div dir=rtl><bdo dir=rtl>ABCDEF</bdo> 123456</div>
diff --git a/css/css-ui/text-overflow-029.html b/css/css-ui/text-overflow-029.html
new file mode 100644
index 0000000..f178011
--- /dev/null
+++ b/css/css-ui/text-overflow-029.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>CSS Basic User Interface Test: text-overflow and bidi interaction</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
+<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
+<link rel="match" href="reference/text-overflow-029-ref.html">
+<meta name="assert" content="When there's content of mixed directionality, text-overflow ellides the characters at the physical end of the line.">
+<meta name="flags" content="">
+<style>
+div {
+  font: 20px monospace;
+  width: 12.3ch; /* slightly more than 12ch because in some browsers (safari) the ellipsis is slightly large than other characters, even in monospace fonts. */
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
+</style>
+
+<div>Test &#x202E;deliafdessap&#x202C;</div>
diff --git a/css/css-writing-modes/available-size-011.html b/css/css-writing-modes/available-size-011.html
new file mode 100644
index 0000000..38aa583
--- /dev/null
+++ b/css/css-writing-modes/available-size-011.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>orthogonal flow parent with max-height</title>
+<meta charset=utf-8>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<meta name="assert" content="If an orthogonal flow's parent doesn't have a definite block size but does have a max block size, use that as the available size">
+<link rel="match" href="reference/available-size-011-ref.html">
+<meta name="flags" content="">
+<style>
+main {
+  max-height: 1em;
+  line-height: 1em;
+}
+div {
+  writing-mode: vertical-rl;
+}
+</style>
+
+<p>This test passes if the word “PASS” (without the quotation marks) appears below, written horizontally from left to right.
+<main><div>S S A P</div></main>
diff --git a/css/css-writing-modes/available-size-012.html b/css/css-writing-modes/available-size-012.html
new file mode 100644
index 0000000..937129e
--- /dev/null
+++ b/css/css-writing-modes/available-size-012.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / max-height + min-height / content height</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-001-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent doesn't have a definite block size and the nearest ancestor scroller does not have a fixed height but does have a fixed max-height, use that, increased by min-height if it exists and is larger. (same as -001, with min-height)">
+<meta name="flags" content="">
+<style>
+body > div {
+  font-family: monospace; /* to be able to reliably measure things in ch*/
+  font-size: 20px;
+  max-height: 4ch; /* **max**-height does not give the element a definite block size */
+  min-height: 8ch;
+  overflow: hidden;
+  color: transparent;
+  position: relative; /* to act as a container of #red */
+  padding: 1ch 0;
+}
+
+div > div { writing-mode: vertical-rl; }
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+}
+
+#red { /* Not necessary when when comparing to the reference, but makes human judgement easier */
+  position: absolute;
+  background: red;
+  left: 0; top: 1ch;
+  writing-mode: vertical-rl;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<div>
+  <aside id="red">0</aside>
+  <div>0 0 0 0 <span>0</span> 0 0 0</div> <!-- If this div take its height from
+  the min-height of its parent, it should wrap just right for the green 0 to
+  overlap with the red one. -->
+</div>
diff --git a/css/css-writing-modes/available-size-013.html b/css/css-writing-modes/available-size-013.html
new file mode 100644
index 0000000..1ffd656
--- /dev/null
+++ b/css/css-writing-modes/available-size-013.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / height + min-height/ content height</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-001-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent doesn't have a definite block size and the nearest ancestor scroller does have a fixed height, use that, increased by the min-height if it is set and is larger (same as -003, with min-height).">
+<meta name="flags" content="">
+<style>
+body > div {
+  font-family: monospace; /* to be able to reliably measure things in ch*/
+  font-size: 20px;
+  height: 4ch;
+  min-height: 8ch;
+  width: 300px; /* Shrinkwrapping this div is not what we're interested in testing here, so give it a width. See nested-orthogonal-001.html for that. */
+  overflow: hidden;
+  color: transparent;
+  position: relative; /* to act as a container of #red */
+  writing-mode: vertical-lr;
+  padding: 1ch 0;
+}
+
+div > div { padding-bottom: 2ch; } /* so that the content height of the parent and of the fixed size scrolling ancestor are different */
+div > div > div { writing-mode: vertical-rl; }
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+}
+
+#red { /* Not necessary when when comparing to the reference, but makes human judgement easier */
+  position: absolute;
+  background: red;
+  left: 0; top: 1ch;
+  writing-mode: vertical-rl;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<div>
+  <aside id="red">0</aside>
+  <div><div>0 0 0 0 <span>0</span> 0 0 0</div></div> <!-- If this div take its height from
+  the height of its scrollable ancestor, it should wrap just right for the green 0 to
+  overlap with the red one. -->
+</div>
diff --git a/css/css-writing-modes/available-size-014.html b/css/css-writing-modes/available-size-014.html
new file mode 100644
index 0000000..e0e41fb
--- /dev/null
+++ b/css/css-writing-modes/available-size-014.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / height + min-height / not remaining size</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-001-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent doesn't have a definite block size and the nearest ancestor scroller does a have fixed height, use that whole height increased by min-height if that's larger, even if some other content already consumes some of it (same as -005, with min-height).">
+<meta name="flags" content="">
+<style>
+body > div {
+  font-family: monospace; /* to be able to reliably measure things in ch*/
+  font-size: 20px;
+  height: 4ch;
+  min-height: 8ch;
+  width: 300px; /* Shrinkwrapping this div is not what we're interested in testing here, so give it a width. See nested-orthogonal-001.html for that. */
+  overflow: hidden;
+  color: transparent;
+  position: relative; /* to act as a container of #red */
+}
+
+div > div { padding-bottom: 2ch; } /* so that the content height of the parent and of the fixed size scrolling ancestor are different */
+div > div > div { writing-mode: vertical-rl; }
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+}
+
+#red { /* Not necessary when when comparing to the reference, but makes human judgement easier */
+  position: absolute;
+  background: red;
+  left: 0;
+  writing-mode: vertical-rl;
+  z-index: -1;
+  top: 1ch;
+}
+#spacer { /* shrinks the remaining space of the parent div. */
+  height: 1ch;
+  width: 100%;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<div>
+  <aside id="red">0</aside>
+  <div><aside id="spacer"></aside><div>0 0 0 0 <span>0</span> 0 0 0</div></div>
+  <!-- If the inner div take its height from the height of its scrollable
+  ancestor, it should wrap just right for the green 0 to overlap with the red
+  one. If instead it takes it size from the remaining height after having
+  removed #spacer, or does some other calculation that takes #spacer into
+  account, it won't line up with #red.-->
+</div>
diff --git a/css/css-writing-modes/available-size-015.html b/css/css-writing-modes/available-size-015.html
new file mode 100644
index 0000000..f7cb13b
--- /dev/null
+++ b/css/css-writing-modes/available-size-015.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / ICB / tall max-height + min-height scroller</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-002-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent doesn't have a definite block size, and there's a scroller with max-height and min-height, and max-height is smaller than the ICB but min-height is larger than the ICB, use the ICB as the available space (same as -008, with min-height).">
+<meta name="flags" content="">
+<style>
+body { margin-top: 0; margin-bottom: 0; } /* Shouldn't matter, but in some browsers does. -007 tests this aspect specifically. */
+div {
+  writing-mode: vertical-rl;
+  font-family: monospace;
+  font-size: 20px;
+  position: relative; /* to be a container for #red*/
+}
+.spacer { /* using 5 spacers of 20vh each instead of a single large one, so
+             that the line would wrap between spacers if it ends up being
+             shorter thatn 100vh*/
+  display: inline-block;
+  height: calc(20vh - 0.1px); /*Using this instead of 20vh, to account for accumulation of rounding errors, that might make 5*20vh taller than 100vh in some browsers*/
+}
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+  color: transparent;
+}
+
+#red { /* Not necessary when when comparing to the reference, but makes human judgement easier */
+  position: absolute;
+  background: red;
+  writing-mode: vertical-rl;
+  z-index: -1;
+  font-family: monospace;
+  font-size: 20px;
+  left: 0; top: 0;
+}
+
+section {
+  overflow: hidden;
+  max-height: 40vh;
+  min-height: 120vh;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<section>
+<div><aside id="red">0</aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside> <span>0</span></div>
+</section>
diff --git a/css/css-writing-modes/available-size-016.html b/css/css-writing-modes/available-size-016.html
new file mode 100644
index 0000000..c3c388e
--- /dev/null
+++ b/css/css-writing-modes/available-size-016.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / ICB / tall height + min-height scroller</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-002-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent doesn't have a definite block size, and there's a scroller with height and min-height, and height is smaller than the ICB but min-height is larger than the ICB, use the ICB as the available space (same as -009, with min-height).">
+<meta name="flags" content="">
+<style>
+body { margin-top: 0; margin-bottom: 0; } /* Shouldn't matter, but in some browsers does. -007 tests this aspect specifically. */
+div {
+  writing-mode: vertical-rl;
+  font-family: monospace;
+  font-size: 20px;
+  position: relative; /* to be a container for #red*/
+}
+.spacer { /* using 5 spacers of 20vh each instead of a single large one, so
+             that the line would wrap between spacers if it ends up being
+             shorter thatn 100vh*/
+  display: inline-block;
+  height: calc(20vh - 0.1px); /*Using this instead of 20vh, to account for accumulation of rounding errors, that might make 5*20vh taller than 100vh in some browsers*/
+}
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+  color: transparent;
+}
+
+#red { /* Not necessary when when comparing to the reference, but makes human judgement easier */
+  position: absolute;
+  background: red;
+  writing-mode: vertical-rl;
+  z-index: -1;
+  font-family: monospace;
+  font-size: 20px;
+  left: 0; top: 0;
+}
+
+section {
+  overflow: hidden;
+  writing-mode: vertical-rl;
+  height: 40vh;
+  min-height: 120vh;
+}
+section > section {
+  writing-mode: horizontal-tb;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<section>
+<div><aside id="red">0</aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside><aside class="spacer"></aside> <span>0</span></div>
+</section>
diff --git a/css/css-writing-modes/available-size-017.html b/css/css-writing-modes/available-size-017.html
new file mode 100644
index 0000000..79f1b85
--- /dev/null
+++ b/css/css-writing-modes/available-size-017.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / height + min-height parent</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-002-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent has a height and a min-height larger than the height, use min-height as the available size.">
+<meta name="flags" content="">
+<style>
+body > div {
+  font-family: monospace; /* to be able to reliably measure things in ch*/
+  font-size: 20px;
+  height: 4ch;
+  min-height: 8ch;
+  color: transparent;
+  position: relative; /* to act as a container of #green */
+}
+
+div > div { writing-mode: vertical-rl; }
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+}
+
+#red {
+  position: absolute;
+  background: red;
+  left: 0;
+  writing-mode: vertical-rl;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<div>
+  <aside id="red">0</aside>
+  <div>0 0 0 0 <span>0</span> 0 0 0</div> <!-- If this div takes its height from
+  the min-height of its parent (which it should)
+  it should wrap just right for the green 0 to
+  overlap with the red one. -->
+</div>
diff --git a/css/css-writing-modes/available-size-018.html b/css/css-writing-modes/available-size-018.html
new file mode 100644
index 0000000..4e86db7
--- /dev/null
+++ b/css/css-writing-modes/available-size-018.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Testing Available Space in Orthogonal Flows / max-height + min-height parent</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto">
+<link rel="match" href="reference/available-size-002-ref.html">
+<meta name="assert" content="When an orthogonal flow's parent has a max-height and a min-height larger than the height, use min-height as the available size.">
+<meta name="flags" content="">
+<style>
+body > div {
+  font-family: monospace; /* to be able to reliably measure things in ch*/
+  font-size: 20px;
+  max-height: 4ch;
+  min-height: 8ch;
+  color: transparent;
+  position: relative; /* to act as a container of #green */
+}
+
+div > div { writing-mode: vertical-rl; }
+
+span {
+  background: green;
+  display: inline-block; /* This should not change it's size or position, but makes the size of the background predictable*/
+}
+
+#red {
+  position: absolute;
+  background: red;
+  left: 0;
+  writing-mode: vertical-rl;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a <strong>green rectangle</strong> below and <strong>no red</strong>.
+
+<div>
+  <aside id="red">0</aside>
+  <div>0 0 0 0 <span>0</span> 0 0 0</div> <!-- If this div takes its height from
+  the min-height of its parent (which it should)
+  it should wrap just right for the green 0 to
+  overlap with the red one. -->
+</div>
diff --git a/css/css-writing-modes/float-lft-orthog-htb-in-vlr-002.xht b/css/css-writing-modes/float-lft-orthog-htb-in-vlr-002.xht
index 690dc78..6cef03a 100644
--- a/css/css-writing-modes/float-lft-orthog-htb-in-vlr-002.xht
+++ b/css/css-writing-modes/float-lft-orthog-htb-in-vlr-002.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-lft-orthog-htb-in-vlr-002-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-lft-orthog-htb-in-vrl-002.xht b/css/css-writing-modes/float-lft-orthog-htb-in-vrl-002.xht
index d11a2f9..977c653 100644
--- a/css/css-writing-modes/float-lft-orthog-htb-in-vrl-002.xht
+++ b/css/css-writing-modes/float-lft-orthog-htb-in-vrl-002.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-lft-orthog-htb-in-vrl-002-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-lft-orthog-vlr-in-htb-002.xht b/css/css-writing-modes/float-lft-orthog-vlr-in-htb-002.xht
index d760b49..e6229f1 100644
--- a/css/css-writing-modes/float-lft-orthog-vlr-in-htb-002.xht
+++ b/css/css-writing-modes/float-lft-orthog-vlr-in-htb-002.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-lft-orthog-vlr-in-htb-002-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-lft-orthog-vrl-in-htb-002.xht b/css/css-writing-modes/float-lft-orthog-vrl-in-htb-002.xht
index ac4462c..63f4359 100644
--- a/css/css-writing-modes/float-lft-orthog-vrl-in-htb-002.xht
+++ b/css/css-writing-modes/float-lft-orthog-vrl-in-htb-002.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-lft-orthog-vrl-in-htb-002-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-rgt-orthog-htb-in-vlr-003.xht b/css/css-writing-modes/float-rgt-orthog-htb-in-vlr-003.xht
index e11aad4..29937cd 100644
--- a/css/css-writing-modes/float-rgt-orthog-htb-in-vlr-003.xht
+++ b/css/css-writing-modes/float-rgt-orthog-htb-in-vlr-003.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-rgt-orthog-htb-in-vlr-003-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-rgt-orthog-htb-in-vrl-003.xht b/css/css-writing-modes/float-rgt-orthog-htb-in-vrl-003.xht
index 89cde7b..518b1f4 100644
--- a/css/css-writing-modes/float-rgt-orthog-htb-in-vrl-003.xht
+++ b/css/css-writing-modes/float-rgt-orthog-htb-in-vrl-003.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-rgt-orthog-htb-in-vrl-003-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-rgt-orthog-vlr-in-htb-003.xht b/css/css-writing-modes/float-rgt-orthog-vlr-in-htb-003.xht
index 09e5979..26589d7 100644
--- a/css/css-writing-modes/float-rgt-orthog-vlr-in-htb-003.xht
+++ b/css/css-writing-modes/float-rgt-orthog-vlr-in-htb-003.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-rgt-orthog-vlr-in-htb-003-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/float-rgt-orthog-vrl-in-htb-003.xht b/css/css-writing-modes/float-rgt-orthog-vrl-in-htb-003.xht
index 203c16e..9ded7b3 100644
--- a/css/css-writing-modes/float-rgt-orthog-vrl-in-htb-003.xht
+++ b/css/css-writing-modes/float-rgt-orthog-vrl-in-htb-003.xht
@@ -15,7 +15,7 @@
   -->
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#auto-multicol" title="7.3.2 Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="float-rgt-orthog-vrl-in-htb-003-ref.xht" />
 
   <meta content="" name="flags" />
diff --git a/css/css-writing-modes/reference/available-size-011-ref.html b/css/css-writing-modes/reference/available-size-011-ref.html
new file mode 100644
index 0000000..ef66b4e
--- /dev/null
+++ b/css/css-writing-modes/reference/available-size-011-ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>CSS writing mode test reference</title>
+<meta charset=utf-8>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<style>
+div {
+  line-height: 1em;
+  height: 1em;
+  writing-mode: vertical-rl;
+}
+</style>
+
+<p>This test passes if the word “PASS” (without the quotation marks) appears below, written horizontally from left to right.
+<div>S S A P</div>
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht
index 1f2d1f1..4a651f8 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-001.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-001-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-003.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-003.xht
index 7e5b672..f8fe8bf 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-003.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-003.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht
index 859d704..2111a8b 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-004-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-006.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-006.xht
index dde973c..52bdd50 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-006.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-006.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-007.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-007.xht
index d76033e..57d3975 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-007.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-007.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-007-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-008.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-008.xht
index 1a0d496..e340adb 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-008.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-008.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-008-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-009.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-009.xht
index 1749520..c156228 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-009.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-009.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-010.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-010.xht
index 716a81a..6d5eb25 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-010.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-010.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-010-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-011.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-011.xht
index a7d0cef..31ada5d 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-011.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-011.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-011-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-012.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-012.xht
index d838466..ce597bd 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-012.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-012.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-013.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-013.xht
index 645746c..8a8a4f3 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-013.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-013.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-013-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-015.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-015.xht
index cab989a..9a92dcb 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-015.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-015.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-016.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-016.xht
index 57059d2..fae31d7 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-016.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-016.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-016-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-018.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-018.xht
index 8502cf0..25a2626 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-018.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-018.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-019.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-019.xht
index 5490888..05a6479 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-019.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-019.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-019-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-020.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-020.xht
index 1667163..f950c77 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-020.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-020.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-020-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-021.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-021.xht
index ef18ccb..94e52c4 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-021.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-021.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-022.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-022.xht
index c945168..39bc330 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-022.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-022.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-022-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-023.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-023.xht
index 21e84bd..4d51832 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-023.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-023.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-023-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vlr-024.xht b/css/css-writing-modes/sizing-orthog-htb-in-vlr-024.xht
index 125ce02..a0e1071 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vlr-024.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vlr-024.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-lr' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-001.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-001.xht
index 94f5128..2e9fe94 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-001.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-001.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-001-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-13T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-003.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-003.xht
index a59172d..828c1e5 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-003.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-003.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-19T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-004.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-004.xht
index 9ac176a..4a20cd0 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-004.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-004.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-004-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-19T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-006.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-006.xht
index c1e3f57..4328830 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-006.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-006.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-19T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-007.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-007.xht
index 7bda42b..203883d 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-007.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-007.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-007-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht
index 9b214c9..f9a3a15 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-008.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-008-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-009.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-009.xht
index d262bb6..36d7d01 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-009.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-009.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-19T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-010.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-010.xht
index 842ab97..2defea2 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-010.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-010.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-010-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-19T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-011.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-011.xht
index c843969..3d03d10 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-011.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-011.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-011-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-012.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-012.xht
index a218bdf..8e43062 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-012.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-012.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-013.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-013.xht
index 1fb1eac..74ce5be 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-013.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-013.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-013-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-015.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-015.xht
index 8c4193c..b616855 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-015.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-015.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-015-ref.xht" />
 
 
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-016.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-016.xht
index 20ec773..a9e5dbc 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-016.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-016.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vlr-016-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-018.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-018.xht
index c0a77d8..5d28446 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-018.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-018.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside auto-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-019.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-019.xht
index 6b168bf..38ea05e 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-019.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-019.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-019-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-020.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-020.xht
index c3a6fa9..0bb3847 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-020.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-020.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-020-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-021.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-021.xht
index 5e8acd5..23555fb 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-021.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-021.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-022.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-022.xht
index 6c30cf7..1d14778 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-022.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-022.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-022-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-023.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-023.xht
index 47ed5af..c2b6176 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-023.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-023.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-023-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-htb-in-vrl-024.xht b/css/css-writing-modes/sizing-orthog-htb-in-vrl-024.xht
index 7b06bd8..811110e 100644
--- a/css/css-writing-modes/sizing-orthog-htb-in-vrl-024.xht
+++ b/css/css-writing-modes/sizing-orthog-htb-in-vrl-024.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'horizontal-tb' block with 'auto' inline size inside definite-sized 'vertical-rl' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-htb-in-vrl-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-12-20T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-001.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-001.xht
index 49cfa83..1136196 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-001.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-001.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-001-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-003.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-003.xht
index 357c517..4f26ccf 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-003.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-003.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-004.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-004.xht
index 318c97c..0454836 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-004.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-004.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-004-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-006.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-006.xht
index 366749f..c58868f 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-006.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-006.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-007.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-007.xht
index b52ae55..54e3828 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-007.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-007.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-007-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-008.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-008.xht
index 187a48d..6f01a71 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-008.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-008.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-008-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-009.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-009.xht
index 8f81bce..47c4c1e 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-009.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-009.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-009-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-010.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-010.xht
index db786b2..de7495f 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-010.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-010.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-010-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-011.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-011.xht
index e973ee3..397781c 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-011.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-011.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-011-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-012.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-012.xht
index e1eb5f8..58149a1 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-012.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-012.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-012-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-013.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-013.xht
index cd42dd6..844b28c 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-013.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-013.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-013-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-015.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-015.xht
index 9d9de14..71b6fa6 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-015.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-015.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-016.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-016.xht
index e26e14d..341d4b6 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-016.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-016.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-016-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-018.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-018.xht
index 51c0bdd..c4d6f12 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-018.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-018.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-019.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-019.xht
index 86760b0..09c5923 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-019.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-019.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-019-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-020.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-020.xht
index 1b8d5ab..afa9a73 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-020.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-020.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-020-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-021.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-021.xht
index 1b37060..8bbf256 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-021.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-021.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-022.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-022.xht
index bb1a9af..9e15c25 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-022.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-022.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-022-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-023.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-023.xht
index 9cfb956..566da43 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-023.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-023.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-023-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vlr-in-htb-024.xht b/css/css-writing-modes/sizing-orthog-vlr-in-htb-024.xht
index 156d9e6..5ca1c96 100644
--- a/css/css-writing-modes/sizing-orthog-vlr-in-htb-024.xht
+++ b/css/css-writing-modes/sizing-orthog-vlr-in-htb-024.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-lr' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vlr-in-htb-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-10-04T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-001.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-001.xht
index c19ab6c..0d41350 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-001.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-001.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-001-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-02T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-003.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-003.xht
index a610020..22209ba 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-003.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-003.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-003-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-08T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-004.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-004.xht
index 478a6a4..ee1d59d 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-004.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-004.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-004-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-006.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-006.xht
index 80955af..d3576e9 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-006.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-006.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-006-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-007.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-007.xht
index fb3c91d..505d046 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-007.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-007.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-007-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-008.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-008.xht
index 7df2838..1d71744 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-008.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-008.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-008-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-009.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-009.xht
index 399ce8f..0073da6 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-009.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-009.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-009-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-010.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-010.xht
index 5b0f4fa..71bbd3e 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-010.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-010.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-010-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-011.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-011.xht
index fa2ee6b..77bcedc 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-011.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-011.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-011-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-012.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-012.xht
index 1504ed9..5ba8fb9 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-012.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-012.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-012-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-15T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-013.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-013.xht
index 4ccc0d0..997174e 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-013.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-013.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-013-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-015.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-015.xht
index 4b88733..a61c675 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-015.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-015.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-016.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-016.xht
index bbe2cdb..1e6382c 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-016.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-016.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-016-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-018.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-018.xht
index ab34193..35e9b19 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-018.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-018.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside auto-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-019.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-019.xht
index e4cebf2..0b29bc9 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-019.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-019.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-019-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-020.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-020.xht
index fc88acb..41c4ba9 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-020.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-020.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-020-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-285T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-021.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-021.xht
index 44e085a..9e2e626 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-021.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-021.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-015-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-022.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-022.xht
index a47986d..1feb2c7 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-022.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-022.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-022-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-023.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-023.xht
index fbc6dea..d0c40ba 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-023.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-023.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-023-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/css-writing-modes/sizing-orthog-vrl-in-htb-024.xht b/css/css-writing-modes/sizing-orthog-vrl-in-htb-024.xht
index c4c0b03..d691383 100644
--- a/css/css-writing-modes/sizing-orthog-vrl-in-htb-024.xht
+++ b/css/css-writing-modes/sizing-orthog-vrl-in-htb-024.xht
@@ -7,7 +7,7 @@
   <title>CSS Writing Modes Test: sizing of orthogonal 'vertical-rl' block with 'auto' inline size inside definite-sized 'horizontal-tb' containing block</title>
 
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
-  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#auto-multicol" title="7.3.2. Auto-sizing Block Containers in Orthogonal Flows" />
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto" />
   <link rel="match" href="sizing-orthog-vrl-in-htb-018-ref.xht" />
 
   <meta name="DC.date.created" content="2016-09-28T09:54:03+11:00" scheme="W3CDTF" />
diff --git a/css/cssom/MediaList.html b/css/cssom/MediaList.html
index 17d4319..bddb5c9 100644
--- a/css/cssom/MediaList.html
+++ b/css/cssom/MediaList.html
@@ -44,6 +44,10 @@
         media.mediaText = null;
         assert_equals(media.mediaText, "", "MediaList mediaText attribute should be empty string in case of null");
         assert_equals(media.toString(), "", "MediaList toString method should be empty string in case of null");
+
+        var rule = document.styleSheets[0].cssRules[0];
+        rule.media = "speech";
+        assert_equals(rule.media.mediaText, "speech", "MediaList mediaText attribute should be updated");
     });
     </script>
 </head>
diff --git a/css/cssom/css-style-attr-decl-block.html b/css/cssom/css-style-attr-decl-block.html
new file mode 100644
index 0000000..9b45d66
--- /dev/null
+++ b/css/cssom/css-style-attr-decl-block.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="author" title="Xidorn Quan" href="mailto:me@upsuper.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#css-declaration-blocks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function createTestElement(style) {
+  let wrapper = document.createElement("div");
+  wrapper.innerHTML = `<div id="test" style="${style}"></div>`;
+  return wrapper.querySelector("#test");
+}
+
+test(function() {
+  let elem = createTestElement("z-index: 10;");
+  assert_equals(elem.style.cssText, "z-index: 10;");
+}, "Style attribute should create CSS declaration block based on its content");
+
+test(function() {
+  let elem = createTestElement("z-index: 20;");
+  let style = elem.style;
+  assert_equals(style.cssText, "z-index: 20;");
+  function assert_css_text(value, action) {
+    assert_equals(style.cssText, value, "CSS declaration block after " + action);
+  }
+  elem.setAttribute("style", "z-index: 21;");
+  assert_css_text("z-index: 21;", "changing the style attribute");
+  elem.removeAttribute("style");
+  assert_css_text("", "removing the style attribute");
+  elem.setAttribute("style", "position: absolute;");
+  assert_css_text("position: absolute;", "adding style attribute again");
+}, "Changes to style attribute should reflect on CSS declaration block");
+
+test(function() {
+  let elem = createTestElement("z-index: 30;");
+  let style = elem.style;
+  assert_equals(style.cssText, "z-index: 30;");
+  function assert_attr(value, action) {
+    assert_equals(elem.getAttribute("style"), value, "style attribute after " + action);
+  }
+  style.setProperty("z-index", "31");
+  assert_attr("z-index: 31;", "changing property in CSS declaration block");
+  style.removeProperty("z-index");
+  assert_attr("", "removing property from CSS declaration block");
+  style.setProperty("position", "absolute");
+  assert_attr("position: absolute;", "adding property to CSS declaration block");
+  style.cssText = "z-index: 32;";
+  assert_attr("z-index: 32;", "changing cssText");
+  style.cssText = "z-index: 33; invalid";
+  assert_attr("z-index: 33;", "changing cssText to a partial invalid value");
+}, "Changes to CSS declaration block should reflect on style attribute");
+
+test(function() {
+  let elem = createTestElement("z-index: 40;");
+  let style = elem.style;
+  assert_equals(style.cssText, "z-index: 40;");
+  // Create an observer for the element.
+  let observer = new MutationObserver(function() {});
+  observer.observe(elem, {attributes: true, attributeOldValue: true});
+  function assert_record_with_old_value(oldValue, action) {
+    let records = observer.takeRecords();
+    assert_equals(records.length, 1, "number of mutation records after " + action);
+    let record = records[0];
+    assert_equals(record.type, "attributes", "mutation type after " + action);
+    assert_equals(record.attributeName, "style", "mutated attribute after " + action);
+    assert_equals(record.oldValue, oldValue, "old value after " + action);
+  }
+  style.setProperty("z-index", "41");
+  assert_record_with_old_value("z-index: 40;", "changing property in CSS declaration block");
+  style.cssText = "z-index: 42;";
+  assert_record_with_old_value("z-index: 41;", "changing cssText");
+  style.cssText = "z-index: 42;";
+  assert_record_with_old_value("z-index: 42;", "changing cssText with the same content");
+  style.removeProperty("z-index");
+  assert_record_with_old_value("z-index: 42;", "removing property from CSS declaration block");
+  // Mutation to shorthand properties should also trigger only one mutation record.
+  style.setProperty("margin", "1px");
+  assert_record_with_old_value("", "adding shorthand property to CSS declaration block");
+  style.removeProperty("margin");
+  assert_record_with_old_value("margin: 1px;", "removing shorthand property from CSS declaration block");
+  // Final sanity check.
+  assert_equals(elem.getAttribute("style"), "");
+}, "Changes to CSS declaration block should queue mutation record for style attribute");
+
+test(function() {
+  let elem = createTestElement("z-index: 50; invalid");
+  let style = elem.style;
+  assert_equals(style.cssText, "z-index: 50;");
+  // Create an observer for the element.
+  let observer = new MutationObserver(function() {});
+  observer.observe(elem, {attributes: true});
+  function assert_no_record(action) {
+    let records = observer.takeRecords();
+    assert_true(records.length == 0, "expect no record after " + action);
+  }
+  style.setProperty("z-index", "invalid");
+  assert_no_record("setting invalid value to property");
+  // Longhand property.
+  style.removeProperty("position");
+  assert_no_record("removing non-existing longhand property");
+  style.setProperty("position", "");
+  assert_no_record("setting empty string to non-existing longhand property");
+  // Shorthand property.
+  style.removeProperty("margin");
+  assert_no_record("removing non-existing shorthand property");
+  style.setProperty("margin", "");
+  assert_no_record("setting empty string to non-existing shorthand property");
+  // Check that the value really isn't changed.
+  assert_equals(elem.getAttribute("style"), "z-index: 50; invalid",
+                "style attribute after removing non-existing properties");
+}, "Removing non-existing property or setting invalid value on CSS declaration block shouldn't queue mutation record");
+
+test(function() {
+  let elem = createTestElement("background-image: url(./);");
+  let style = elem.style;
+  let base = document.createElement("base");
+  base.href = "/";
+  document.body.appendChild(elem);
+  let originalComputedValue = getComputedStyle(elem).backgroundImage;
+  document.head.appendChild(base);
+  this.add_cleanup(() => {
+    document.head.removeChild(base);
+    document.body.removeChild(elem);
+  });
+  style.setProperty("background-color", "green");
+  assert_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
+                "getComputedStyle(elem).backgroundImage after setting background-color");
+  style.setProperty("background-image", "url(./)");
+  assert_not_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
+                    "getComputedStyle(elem).backgroundImage after setting background-image");
+}, "Changes to CSS declaration block after a base URL change");
+</script>
diff --git a/css/cssom/cssimportrule.html b/css/cssom/cssimportrule.html
index d4a3250..691f886 100644
--- a/css/cssom/cssimportrule.html
+++ b/css/cssom/cssimportrule.html
@@ -14,16 +14,17 @@
     <style id="styleElement" type="text/css">
         @import url("support/a-green.css");
         @import url("support/a-green.css") screen;
+        @page { background-color: red; }
     </style>
 </head>
 <body>
     <div id="log"></div>
 
     <script type="text/javascript">
-        var rule, ruleWithMedia;
+        var styleSheet, ruleList, rule, ruleWithMedia;
         setup(function() {
-            var styleSheet = document.getElementById("styleElement").sheet;
-            var ruleList = styleSheet.cssRules;
+            styleSheet = document.getElementById("styleElement").sheet;
+            ruleList = styleSheet.cssRules;
             rule = ruleList[0];
             ruleWithMedia = ruleList[1];
         });
@@ -83,6 +84,22 @@
             assert_true(ruleWithMedia.media.length > 0);
             assert_equals(ruleWithMedia.media.mediaText, "screen");
         }, "Values of CSSImportRule attributes");
+
+        test(function() {
+            ruleWithMedia.media = "print";
+            assert_equals(ruleWithMedia.media.mediaText, "print");
+        }, "CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards]");
+
+        test(function() {
+            var ruleWithPage = ruleList[2];
+            ruleWithPage.style = "background-color: green;"
+            assert_equals(ruleWithPage.style.cssText, "background-color: green;");
+        }, "CSSStyleDeclaration cssText attribute should be updated due to [PutForwards]");
+
+        test(function() {
+            styleSheet.media = "screen";
+            assert_equals(styleSheet.media.mediaText, "screen");
+        }, "StyleSheet : MediaList mediaText attribute should be updated due to [PutForwards]");
     </script>
 </body>
 </html>
diff --git a/css/cssom/cssom-fontfacerule.html b/css/cssom/cssom-fontfacerule.html
index 0620910..e44c3dc 100644
--- a/css/cssom/cssom-fontfacerule.html
+++ b/css/cssom/cssom-fontfacerule.html
@@ -3,7 +3,7 @@
     <head>
         <title>CSSOM Parsing Test: @font-face rules parsed into CSSOM CSSFontFaceRules</title>
         <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
-        <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-font-face-rule">
+        <link rel="help" href="https://drafts.csswg.org/cssom/#css-rules">
 
         <meta name="flags" content="dom">
 
@@ -44,7 +44,7 @@
 
             test(function(){
 
-                assert_equals(validRules[0].style.src, 'url(http://foo/bar/font.ttf)');
+                assert_equals(validRules[0].style.src, 'url("http://foo/bar/font.ttf")');
                 assert_equals(validRules[1].style.fontFamily, 'STIXGeneral');
 
                 /* unimplemented @font-face properties are not represented in CSSOM */
diff --git a/css/cssom/selectorText-modification-restyle-002.html b/css/cssom/selectorText-modification-restyle-002.html
new file mode 100644
index 0000000..a6b37c2
--- /dev/null
+++ b/css/cssom/selectorText-modification-restyle-002.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>CSSOM: Modify selectorText in a shadow tree stylesheet</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  #container { color: red }
+  .subtree * { color: pink }
+</style>
+<div id="container">
+  <div id="host"></div>
+</div>
+<script>
+  const root = host.attachShadow({mode:"open"});
+  root.innerHTML = "<style>nomatch { color: green }</style><div>Green</div>";
+  const div = root.querySelector("div");
+
+  test(() => {
+    assert_equals(getComputedStyle(div).color, "rgb(255, 0, 0)", "Color should initial be red.");
+  }, "Check initial color.");
+
+  test(() => {
+    // The combination of the .subtree and CSSOM selector style invalidations
+    // caused the Blink implementation to fail an assertion.
+    container.className = "subtree";
+    root.styleSheets[0].cssRules[0].selectorText = "div";
+    assert_equals(getComputedStyle(div).color, "rgb(0, 128, 0)", "Color should be green after stylesheet change.");
+  }, "Check that color changes correctly after shadow stylesheet selector and #container class is changed.");
+</script>
diff --git a/css/mediaqueries/viewport-script-dynamic-ref.html b/css/mediaqueries/viewport-script-dynamic-ref.html
new file mode 100644
index 0000000..e3f1c95
--- /dev/null
+++ b/css/mediaqueries/viewport-script-dynamic-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io">
+<meta name="viewport" content="width=300">
+<style>
+p { color: green; }
+</style>
+<p>Should be green</p>
diff --git a/css/mediaqueries/viewport-script-dynamic.html b/css/mediaqueries/viewport-script-dynamic.html
new file mode 100644
index 0000000..7433877
--- /dev/null
+++ b/css/mediaqueries/viewport-script-dynamic.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: Meta viewport after a script and stylesheets</title>
+<link rel="author" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1424878">
+<link rel="help" href="https://drafts.csswg.org/mediaqueries/#mf-dimensions">
+<link rel="match" href="viewport-script-dynamic-ref.html">
+<style>
+p { color: green; }
+/* Ensure that we initially match it, and stop matching it afterwards */
+@media (min-width: 310px) {
+  p {
+    color: red;
+  }
+}
+</style>
+<!-- The broken script below is the point of the test, see the bugzilla bug. -->
+<script src="intentionally-broken-url.js"></script>
+<meta name="viewport" content="width=300">
+<p>Should be green</p>
diff --git a/css/selectors/matches-nested.html b/css/selectors/matches-nested.html
new file mode 100644
index 0000000..b82d43d
--- /dev/null
+++ b/css/selectors/matches-nested.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Selectors: :matches()</title>
+    <link rel="author" title="Victoria Su" href="mailto:victoriaytsu@google.com">
+    <link rel="help" href="https://drafts.csswg.org/selectors-4/#matches">
+    <meta name="assert" content="This tests that the :matches() selector is effective when nested">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <style>
+      /* Testing that highest specificity is chosen for class outside of :matches() */
+      .a+.b+.c>.e+.d {
+        color: black;
+        font-size: 10px;
+        width: 10px;
+      }
+      .a+:matches(.b+.f, .b+:matches(*, .c>.e, .g, *))+.d {
+        color: red;
+        font-size: 20px;
+      }
+      .a+.b+.c>.e+.d {
+        color: yellow;
+      }
+      /* Testing specificty of a class within :matches() */
+      .a+.c>.e {
+        color: black;
+      }
+      .a+:matches(.b+.f, :matches(.c>.e, .g)) {
+        color: red;
+      }
+      .c>.e {
+        color: black;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="a">
+    </div>
+    <div class="b" id="b2">
+    </div>
+    <div class="c" id="c2">
+      <div class="e">
+      </div>
+      <div class="d" id="d1">
+        Yellow
+      </div>
+    </div>
+    <div class="a">
+    </div>
+    <div class="c" id="c2">
+      <div class="e" id="e1">
+        Red
+      </div>
+    </div>
+    <script>
+
+      var red = "rgb(255, 0, 0)";
+      var yellow = "rgb(255, 255, 0)";
+
+      test(() => {
+        assert_equals(getComputedStyle(d1).color, yellow);
+        assert_equals(getComputedStyle(d1).fontSize, "20px");
+        assert_equals(getComputedStyle(d1).width, "10px");
+      }, "Test nested :matches() chooses highest specificity for class outside :matches().");
+
+      test(() => {
+        assert_equals(getComputedStyle(e1).color, red);
+      }, "Test nested :matches() specificity for class within arguments.");
+
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/css/selectors/matches-specificity.html b/css/selectors/matches-specificity.html
new file mode 100644
index 0000000..41d7251
--- /dev/null
+++ b/css/selectors/matches-specificity.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Selectors: :matches()</title>
+    <link rel="author" title="Victoria Su" href="mailto:victoriaytsu@google.com">
+    <link rel="help" href="https://drafts.csswg.org/selectors-4/#matches">
+    <meta name="assert" content="This tests that the :matches() selector chooses the correct specificity">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <style>
+      .b.c + .d + .q.r + .s + #target {
+        font-size: 10px;
+        height: 10px;
+        width: 10px;
+      }
+      :matches(.a, .b.c + .d, .e) + :matches(* + .p, .q.r + .s, * + .t) + #target {
+        height: 20px;
+        width: 20px;
+      }
+      .b.c + .d + .q.r + .s + #target {
+        width: 30px;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="b c"></div>
+    <div class="a d e"></div>
+    <div class="q r"></div>
+    <div class="p s t"></div>
+    <div id="target"></div>
+    <script>
+
+      test(() => {
+        assert_equals(getComputedStyle(target).width, "30px");
+        assert_equals(getComputedStyle(target).height, "20px");
+        assert_equals(getComputedStyle(target).fontSize, "10px");
+      }, "Test :matches() uses highest possible specificity");
+
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001v.xhtml
new file mode 100644
index 0000000..f1dfdfd
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001v.xhtml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!-- Testcase with blocks as flex items in a horizontal flex container, with
+     various "flex" values and various combinations of the items
+     and with various writing-modes on the items. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing flexbox layout algorithm property on block flex items in a horizontal flex container
+      (with various writing-modes on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-basic-block-horiz-001-ref.xhtml"/>
+    <style>
+      div { height: 100px; }
+      div.flexbox {
+        border: 1px dashed blue;
+        width: 200px;
+        font-size: 10px;
+        display: flex;
+      }
+      div.a {
+        flex: 1 0 30px;
+        background: lightgreen;
+        writing-mode: vertical-lr;
+      }
+      div.b {
+        flex: 2 0 20px;
+        background: yellow;
+        writing-mode: vertical-rl;
+      }
+      div.c {
+        flex: 3 0 40px;
+        background: orange;
+        writing-mode: sideways-lr;
+      }
+      div.flexNone {
+        flex: none;
+        background: pink;
+        writing-mode: vertical-lr;
+      }
+      div.flexBasis {
+        flex: 0 0 20px;
+        background: gray;
+        writing-mode: sideways-rl;
+      }
+      div.spacer {
+        width: 15px;
+        height: 15px;
+        background: purple;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="flexbox"><div class="a"></div><div class="b"/></div>
+    <div class="flexbox"><div class="a"/><div class="c"/></div>
+    <div class="flexbox"><div class="a"/>
+      <div class="flexNone"><div class="spacer"/></div>
+    </div>
+    <div class="flexbox"><div class="b"/><div class="c"/></div>
+    <div class="flexbox"><div class="b"/>
+      <div class="flexNone"><div class="spacer"/><div class="spacer"/></div>
+    </div>
+
+    <div class="flexbox">
+      <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/>
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001v.xhtml
new file mode 100644
index 0000000..da943df
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001v.xhtml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!-- Testcase with blocks as flex items in a vertical flex container, with
+     various "flex" values and various combinations of the items. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing flexbox layout algorithm property on block flex items in a vertical flex container
+      (with various writing-modes on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-basic-block-vert-001-ref.xhtml"/>
+    <style>
+      div { width: 50px; }
+      div.flexbox {
+        float: left;
+        border: 1px dashed blue;
+        height: 200px;
+        font-size: 10px;
+        display: flex;
+        flex-direction: column;
+      }
+      div.a {
+        flex: 1 0 30px;
+        background: lightgreen;
+        writing-mode: vertical-lr;
+      }
+      div.b {
+        flex: 2 0 20px;
+        background: yellow;
+        writing-mode: vertical-rl;
+      }
+      div.c {
+        flex: 3 0 40px;
+        background: orange;
+        writing-mode: sideways-lr;
+      }
+      div.flexNone {
+        flex: none;
+        background: pink;
+        writing-mode: vertical-lr;
+      }
+      div.flexBasis {
+        flex: 0 0 20px;
+        background: gray;
+        writing-mode: sideways-rl;
+      }
+      div.spacer {
+        display: inline-block;
+        width: 15px;
+        height: 15px;
+        background: purple;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="flexbox"><div class="a"></div><div class="b"/></div>
+    <div class="flexbox"><div class="a"/><div class="c"/></div>
+    <div class="flexbox"><div class="a"/>
+      <div class="flexNone"><div class="spacer"/></div>
+    </div>
+    <div class="flexbox"><div class="b"/><div class="c"/></div>
+    <div class="flexbox"><div class="b"/>
+      <div class="flexNone"><div class="spacer"/><div class="spacer"/></div>
+    </div>
+
+    <div class="flexbox">
+      <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/>
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001v.xhtml
new file mode 100644
index 0000000..ad623d7
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001v.xhtml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This test checks that canvas elements behave correctly as flex items.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing flexbox layout algorithm property on canvas flex items in a horizontal flex container
+      (with a vertical writing-mode on the canvas flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-basic-canvas-horiz-001-ref.xhtml"/>
+    <style>
+      div.flexbox {
+        width: 200px;
+        background: lightgreen;
+        display: flex;
+        justify-content: space-between;
+        margin-bottom: 5px;
+        line-height: 8px;
+      }
+      canvas {
+        min-width: 0;
+        width: 10px;
+        height: 20px;
+        border: 1px dotted green;
+        writing-mode: vertical-lr;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- A) One flex item -->
+    <div class="flexbox">
+      <canvas/>
+    </div>
+
+    <!-- B) Text and a canvas (ensure they aren't merged into one flex item) -->
+    <div class="flexbox">
+      some words <canvas/>
+    </div>
+
+    <!-- C) Two canvas elements, getting stretched by different ratios, from 0.
+
+         Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1)
+                                 = 196px
+
+         1st element gets 5/8 of space: 5/8 * 196px = 122.5px
+         1st element gets 3/8 of space: 3/8 * 196px = 73.5px
+      -->
+    <div class="flexbox">
+      <canvas style="flex: 5"/>
+      <canvas style="flex: 3"/>
+    </div>
+
+    <!-- D) Two canvas elements, getting stretched by different ratios, from
+         different flex bases.
+
+         Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px
+         1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px
+         1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px
+      -->
+    <div class="flexbox">
+      <canvas style="width: 33px; flex: 2 auto"/>
+      <canvas style="width: 13px; flex: 3 auto"/>
+    </div>
+
+    <!-- E) Two flex items, getting shrunk by different amounts.
+
+         Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px
+         First element scaled flex ratio = 4 * 150 = 600
+         Second element scaled flex ratio = 3 * 100 = 300
+           * So, total flexibility is 600 + 300 = 900
+
+         1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px
+         2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px
+      -->
+    <div class="flexbox">
+      <canvas style="width: 150px; flex: 1 4 auto"/>
+      <canvas style="width: 100px; flex: 1 3 auto"/>
+    </div>
+
+    <!-- F) Making sure we don't grow past max-width -->
+    <!-- Same as (D), except we've added a max-width on the second element.
+      -->
+    <div class="flexbox">
+      <canvas style="width: 33px; flex: 2 auto"/>
+      <canvas style="width: 13px; max-width: 90px; flex: 3 auto"/>
+    </div>
+
+    <!-- G) Making sure we grow at least as large as min-width -->
+    <!-- Same as (C), except we've added a min-width on the second element.
+      -->
+    <div class="flexbox">
+      <canvas style="width: 33px; flex: 2 auto"/>
+      <canvas style="width: 13px; min-width: 150px; flex: 3 auto"/>
+    </div>
+
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001v.xhtml
new file mode 100644
index 0000000..057f63a
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001v.xhtml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This test checks that canvas elements behave correctly as flex items.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing flexbox layout algorithm property on canvas flex items in a vertical flex container
+      (with a vertical writing-mode on the canvas flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-basic-canvas-vert-001-ref.xhtml"/>
+    <style>
+      div.flexbox {
+        height: 200px;
+        background: lightgreen;
+        display: flex;
+        justify-content: space-between;
+        flex-direction: column;
+        float: left;
+        margin-right: 10px;
+        font: 8px monospace;
+      }
+      canvas {
+        width: 20px;
+        height: 10px;
+        min-height: 0;
+        border: 1px dotted green;
+        writing-mode: vertical-lr;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- A) One flex item -->
+    <div class="flexbox">
+      <canvas/>
+    </div>
+
+    <!-- B) Text and a canvas (ensure they aren't merged into one flex item) -->
+    <div class="flexbox">
+      a b <canvas/>
+    </div>
+
+    <!-- C) Two canvas elements, getting stretched by different ratios, from 0.
+
+         Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1)
+                                 = 196px
+
+         1st element gets 5/8 of space: 5/8 * 196px = 122.5px
+         1st element gets 3/8 of space: 3/8 * 196px = 73.5px
+      -->
+    <div class="flexbox">
+      <canvas style="flex: 5"/>
+      <canvas style="flex: 3"/>
+    </div>
+
+    <!-- D) Two canvas elements, getting stretched by different ratios, from
+         different flex bases.
+
+         Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px
+         1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px
+         1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px
+      -->
+    <div class="flexbox">
+      <canvas style="height: 33px; flex: 2 auto"/>
+      <canvas style="height: 13px; flex: 3 auto"/>
+    </div>
+
+    <!-- E) Two flex items, getting shrunk by different amounts.
+
+         Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px
+         First element scaled flex ratio = 4 * 150 = 600
+         Second element scaled flex ratio = 3 * 100 = 300
+           * So, total flexibility is 600 + 300 = 900
+
+         1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px
+         2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px
+      -->
+    <div class="flexbox">
+      <canvas style="height: 150px; flex: 1 4 auto"/>
+      <canvas style="height: 100px; flex: 1 3 auto"/>
+    </div>
+
+    <!-- F) Making sure we don't grow past max-height -->
+    <!-- Same as (D), except we've added a max-height on the second element.
+      -->
+    <div class="flexbox">
+      <canvas style="height: 33px; flex: 2 auto"/>
+      <canvas style="height: 13px; max-height: 90px; flex: 3 auto"/>
+    </div>
+
+    <!-- G) Making sure we grow at least as large as min-height -->
+    <!-- Same as (C), except we've added a min-height on the second element.
+      -->
+    <div class="flexbox">
+      <canvas style="height: 33px; flex: 2 auto"/>
+      <canvas style="height: 13px; min-height: 150px; flex: 3 auto"/>
+    </div>
+
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001v.html
new file mode 100644
index 0000000..4cc910b
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001v.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on non-stretched flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-001-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: row;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: flex-start;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        writing-mode: vertical-lr;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 0 0 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002v.html
new file mode 100644
index 0000000..0df92f9
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002v.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on non-stretched flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-001-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: column;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: flex-start;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        writing-mode: vertical-lr;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 0 0 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html
new file mode 100644
index 0000000..cb9275f
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on stretched flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-003-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: row;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: stretch;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        writing-mode: vertical-rl;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 0 0 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-004v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-004v.html
new file mode 100644
index 0000000..01c5271
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-004v.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on stretched flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-004-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: column;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: stretch;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        writing-mode: vertical-rl;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 0 0 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005v.html
new file mode 100644
index 0000000..ed1dcac
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005v.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on non-stretched flexible flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-005-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: row;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: flex-start;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        flex: 1;
+        writing-mode: vertical-lr;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 1 1 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006v.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006v.html
new file mode 100644
index 0000000..8c12fad
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006v.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>
+      CSS Test: Testing how explicit main-size & cross-size constraints
+      influence sizing on non-stretched flexible flex item w/ intrinsic ratio
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-main-size">
+    <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#hypothetical-cross-size">
+    <link rel="match" href="flexbox-intrinsic-ratio-006-ref.html">
+    <style>
+      .flexbox {
+        display: flex;
+        flex-direction: column;
+        border: 1px solid black;
+        margin: 0 2px 2px 0; /* (Just for spacing things out, visually) */
+        width: 40px;
+        height: 40px;
+
+        justify-content: flex-start;
+        align-items: flex-start;
+
+        float: left; /* For testing in "rows" */
+      }
+      br { clear: both; }
+
+      .flexbox > * {
+        flex: 1;
+        writing-mode: vertical-lr;
+
+        /* Disable "min-width:auto"/"min-height:auto" to focus purely on
+           later channels of influence. */
+        min-width: 0;
+        min-height: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- NOTE: solidblue.png has an intrinsic size of 16px by 16px. -->
+
+    <!-- Row 1: no special sizing: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png">
+    </div>
+    <br>
+
+    <!-- Row 2: Specified main-size, cross-size, or flex-basis: -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="flex: 1 1 30px">
+    </div>
+    <br>
+
+    <!-- Row 3: min main-size OR min cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              min-height: 34px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 34px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 4: max main-size OR max cross-size, or both -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              max-height: 6px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 6px;
+                                              max-height: 10px">
+    </div>
+    <br>
+
+    <!-- Row 5: min main-size vs. max cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              min-height: 30px">
+    </div>
+    <br>
+
+    <!-- Row 6: min|max main-size vs. explicit cross-size, & vice versa -->
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="min-width: 30px;
+                                              height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 30px;
+                                              max-height: 10px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="max-width: 10px;
+                                              height: 30px">
+    </div>
+    <div class="flexbox">
+      <img src="support/solidblue.png" style="width: 10px;
+                                              min-height: 30px">
+    </div>
+  </body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002v.xhtml
new file mode 100644
index 0000000..a02e340
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002v.xhtml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!-- Testcase with margin/border/padding on flex items
+     and with various writing-modes on the items. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing margins, borders, and padding on flex items in a horizontal flex container
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-mbp-horiz-002-ref.xhtml"/>
+    <style>
+      div { height: 100px; border: 0; }
+      div.flexbox {
+        width: 200px;
+        font-size: 10px;
+        display: flex;
+      }
+      div.a {
+        flex: 1 0 9px;
+        background: lightgreen;
+        margin-left: 1px;
+        margin-right: 3px;
+        border-style: dotted;
+        border-left-width: 2px;
+        border-right-width: 4px;
+        padding-left: 5px;
+        padding-right: 6px;
+        writing-mode: vertical-lr;
+      }
+      div.b {
+        flex: 2 0 1px;
+        background: yellow;
+        margin-left: 2px;
+        margin-right: 4px;
+        border-style: dashed;
+        border-left-width: 7px;
+        border-right-width: 3px;
+        padding-left: 1px;
+        padding-right: 2px;
+        writing-mode: vertical-rl;
+      }
+      div.c {
+        flex: 3 0 40px;
+        background: orange;
+        writing-mode: sideways-lr;
+      }
+      div.flexNone {
+        flex: none;
+        background: pink;
+        writing-mode: vertical-lr;
+      }
+      div.flexBasis {
+        flex: 0 0 20px;
+        background: gray;
+        writing-mode: sideways-rl;
+      }
+      div.spacer {
+        width: 15px;
+        height: 15px;
+        background: purple;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="flexbox"><div class="a"></div><div class="b"/></div>
+    <div class="flexbox"><div class="a"/><div class="c"/></div>
+    <div class="flexbox"><div class="a"/>
+      <div class="flexNone"><div class="spacer"/></div>
+    </div>
+    <div class="flexbox"><div class="b"/><div class="c"/></div>
+    <div class="flexbox"><div class="b"/>
+      <div class="flexNone"><div class="spacer"/><div class="spacer"/></div>
+    </div>
+
+    <div class="flexbox">
+      <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/>
+    </div>
+  </body>
+</html>
+
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003v.xhtml b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003v.xhtml
new file mode 100644
index 0000000..8baef30
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003v.xhtml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!-- Testcase with border/padding on a flex container and on its children -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+      CSS Test: Testing borders and padding on a horizontal flex container and its flex items
+      (with a vertical writing-mode on the flex items).
+    </title>
+    <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/>
+    <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/>
+    <link rel="match" href="flexbox-mbp-horiz-003-ref.xhtml"/>
+    <style>
+      div { height: 20px; border: 0; }
+      div.flexbox {
+        width: 200px;
+        display: flex;
+        margin-bottom: 2px;
+      }
+
+      <!-- customizations for flex container border/padding -->
+      .borderA {
+        border-style: dashed;
+        border-color: purple;
+        border-top-width: 6px;
+        border-right-width: 4px;
+        border-bottom-width: 2px;
+        border-left-width: 8px;
+      }
+
+      .borderB {
+        border-style: dashed;
+        border-color: purple;
+        border-top-width: 4px;
+        border-right-width: 5px;
+        border-bottom-width: 6px;
+        border-left-width: 7px;
+      }
+
+      .paddingA {
+        padding: 4px 3px 2px 1px;
+      }
+
+      .paddingB {
+        padding: 8px 11px 14px 17px;
+      }
+
+      div.child1 {
+        flex: 1 0 24px;
+        background: lightgreen;
+        border-style: dotted;
+        border-left-width: 2px;
+        border-right-width: 4px;
+        writing-mode: vertical-rl;
+      }
+      div.child2 {
+        flex: 2 0 10px;
+        background: yellow;
+        border-style: dashed;
+        border-left-width: 7px;
+        border-right-width: 3px;
+        writing-mode: vertical-lr;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="flexbox borderA"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox borderA paddingA"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox borderA paddingB"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox borderB"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox borderB paddingA"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox borderB paddingB"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox paddingA"
+         ><div class="child1"/><div class="child2"/></div>
+    <div class="flexbox paddingB"
+         ><div class="child1"/><div class="child2"/></div>
+  </body>
+</html>
+
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010-ref.html
new file mode 100644
index 0000000..e488416
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010-ref.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+  .container > .hl, .container > .hr {
+    /* In the testcase, these items are stretched vertically
+       via the default "align-self:stretch" behavior, and because
+       they have a height of "auto".
+       (The rest of the items have a non-auto height from "inline-size"
+       and their vertical writing-mode, so those ones do not stretch.) */
+    height: 118px;
+  }
+
+  .container.hl > * { float: left; }
+  .container.hr > * { float: right; }
+
+  </style>
+</head>
+<body>
+
+<div class="container hl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010.html
new file mode 100644
index 0000000..55c50e8
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a horizontal row-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-010-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container hl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011-ref.html
new file mode 100644
index 0000000..8b92649
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011-ref.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+
+    /* This really floats to top ('logical left'), since all the containers
+       have a vertical writing mode. */
+    float: left;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+
+  .container > .vl,     .container > .vr,
+  .container > .vl_rtl, .container > .vr_rtl {
+    /* In the testcase, these items are stretched horizontally
+       via the default "align-self:stretch" behavior, and because
+       they have a width of "auto".
+       (The rest of the items have a non-auto width from "inline-size"
+       and their horizontal writing-mode, so those ones do not stretch.) */
+    width: 126px;
+  }
+  </style>
+</head>
+<body>
+
+<div class="container vl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html
new file mode 100644
index 0000000..6114e19
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a vertical row-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-011-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container vl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012-ref.html
new file mode 100644
index 0000000..c03358b
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012-ref.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+
+    /* This really floats to top ('logical left'), since all the containers
+       have a vertical writing mode. */
+    float: right;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+
+  .container > .vl,     .container > .vr,
+  .container > .vl_rtl, .container > .vr_rtl {
+    /* In the testcase, these items are stretched horizontally
+       via the default "align-self:stretch" behavior, and because
+       they have a width of "auto".
+       (The rest of the items have a non-auto width from "inline-size"
+       and their horizontal writing-mode, so those ones do not stretch.) */
+    width: 126px;
+  }
+  </style>
+</head>
+<body>
+
+<div class="container vl_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html
new file mode 100644
index 0000000..a7b05f1
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a vertical row-oriented flex container
+    with 'direction' flipped.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-012-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container vl_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013-ref.html
new file mode 100644
index 0000000..2a27003
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013-ref.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  nocollapse {
+    /* special element to disable margin-collapsing */
+    display: block;
+    overflow: hidden;
+    height: 0;
+  }
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+  .container > .vl,     .container > .vr,
+  .container > .vl_rtl, .container > .vr_rtl {
+    /* In the testcase, these items are stretched horizontally
+       via the default "align-self:stretch" behavior, and because
+       they have a width of "auto".
+       (The rest of the items have a non-auto width from "inline-size"
+       and their horizontal writing-mode, so those ones do not stretch.) */
+    width: 126px;
+  }
+  </style>
+</head>
+<body>
+
+<div class="container hl">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container hl">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+<div class="container hr">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container hr">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013.html
new file mode 100644
index 0000000..a5d483c
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a horizontal column-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-013-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    float: left;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 500px;
+    width: 150px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container hl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container hr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014-ref.html
new file mode 100644
index 0000000..4e4f590
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014-ref.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  nocollapse {
+    /* special element to disable margin-collapsing */
+    display: block;
+    overflow: hidden;
+    height: 0;
+  }
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+  .container > .hl, .container > .hr {
+    /* In the testcase, these items are stretched vertically
+       via the default "align-self:stretch" behavior, and because
+       they have a height of "auto".
+       (The rest of the items have a non-auto height from "inline-size"
+       and their vertical writing-mode, so those ones do not stretch.) */
+    height: 118px;
+  }
+  </style>
+</head>
+<body>
+
+<div class="container vl">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container vl">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+<div class="container vr">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container vr">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html
new file mode 100644
index 0000000..1a54432
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a vertical column-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-014-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container vl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015-ref.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015-ref.html
new file mode 100644
index 0000000..a236ca1
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015-ref.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: block;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  nocollapse {
+    /* special element to disable margin-collapsing */
+    display: block;
+    overflow: hidden;
+    height: 0;
+  }
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+
+  .container > .hl, .container > .hr {
+    /* In the testcase, these items are stretched vertically
+       via the default "align-self:stretch" behavior, and because
+       they have a height of "auto".
+       (The rest of the items have a non-auto height from "inline-size"
+       and their vertical writing-mode, so those ones do not stretch.) */
+    height: 118px;
+  }
+  </style>
+</head>
+<body>
+
+<div class="container vl_rtl">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container vl_rtl">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+<div class="container vr_rtl">
+  <span class="hl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hl big">d e</span>        <nocollapse></nocollapse>
+  <span class="hr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="hr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vl big">d e</span>        <nocollapse></nocollapse>
+</div>
+<div class="container vr_rtl">
+  <span class="vr small">a b c</span>    <nocollapse></nocollapse>
+  <span class="vr big">d e</span>        <nocollapse></nocollapse>
+  <span class="vl_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vl_rtl big">d e</span>    <nocollapse></nocollapse>
+  <span class="vr_rtl small">a b c</span><nocollapse></nocollapse>
+  <span class="vr_rtl big">d e</span>    <nocollapse></nocollapse>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html
new file mode 100644
index 0000000..af1f9b8
--- /dev/null
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing a mix of flex items with various values for
+    'writing-mode' / 'direction' in a vertical column-oriented flex container
+    with 'direction' flipped.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#flex-direction-property">
+  <link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode">
+  <link rel="match" href="flexbox-writing-mode-015-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 150px;
+    width: 500px;
+  }
+
+  span {
+    display: block;
+    background: lightgrey;
+    border: 2px solid black;
+    /* If browser supports it, signal the inline direction with border color: */
+    border-block-start-color: orange;
+    border-inline-start-color: lime;
+
+    margin: 11px 13px 17px 7px;
+    inline-size: 6px;
+  }
+
+  .small { font: 12px Ahem; }
+  .big   { font: 20px Ahem; }
+
+  .hl  { writing-mode: horizontal-tb;  direction: ltr; }
+  .hr  { writing-mode: horizontal-tb;  direction: rtl; }
+  .vl  { writing-mode: vertical-lr;    direction: ltr; }
+  .vr  { writing-mode: vertical-rl;    direction: ltr; }
+  .vl_rtl { writing-mode: vertical-lr; direction: rtl; }
+  .vr_rtl { writing-mode: vertical-rl; direction: rtl; }
+  </style>
+</head>
+<body>
+
+<div class="container vl_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vl_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="hl small">a b c</span>
+  <span class="hl big">d e</span>
+  <span class="hr small">a b c</span>
+  <span class="hr big">d e</span>
+  <span class="vl small">a b c</span>
+  <span class="vl big">d e</span>
+</div>
+<div class="container vr_rtl">
+  <span class="vr small">a b c</span>
+  <span class="vr big">d e</span>
+  <span class="vl_rtl small">a b c</span>
+  <span class="vl_rtl big">d e</span>
+  <span class="vr_rtl small">a b c</span>
+  <span class="vr_rtl big">d e</span>
+</div>
+
+</body>
+</html>
diff --git a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list
index cb4c09e..d9252b2 100644
--- a/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list
+++ b/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list
@@ -61,11 +61,15 @@
 
 # Basic tests with with blocks as flex items
 == flexbox-basic-block-horiz-001.xhtml flexbox-basic-block-horiz-001-ref.xhtml
+== flexbox-basic-block-horiz-001v.xhtml flexbox-basic-block-horiz-001-ref.xhtml
 == flexbox-basic-block-vert-001.xhtml flexbox-basic-block-vert-001-ref.xhtml
+== flexbox-basic-block-vert-001v.xhtml flexbox-basic-block-vert-001-ref.xhtml
 
 # Tests for basic handling of <canvas>/<img>/etc as a flex item
 == flexbox-basic-canvas-horiz-001.xhtml   flexbox-basic-canvas-horiz-001-ref.xhtml
+== flexbox-basic-canvas-horiz-001v.xhtml  flexbox-basic-canvas-horiz-001-ref.xhtml
 == flexbox-basic-canvas-vert-001.xhtml    flexbox-basic-canvas-vert-001-ref.xhtml
+== flexbox-basic-canvas-vert-001v.xhtml   flexbox-basic-canvas-vert-001-ref.xhtml
 == flexbox-basic-fieldset-horiz-001.xhtml flexbox-basic-fieldset-horiz-001-ref.xhtml
 == flexbox-basic-fieldset-vert-001.xhtml  flexbox-basic-fieldset-vert-001-ref.xhtml
 == flexbox-basic-iframe-horiz-001.xhtml   flexbox-basic-iframe-horiz-001-ref.xhtml
@@ -107,11 +111,17 @@
 # (Note that tests 001 and 002 share a reference case; they render the same,
 # because they don't do any direction-specific stretching/flexing.)
 == flexbox-intrinsic-ratio-001.html flexbox-intrinsic-ratio-001-ref.html
+== flexbox-intrinsic-ratio-001v.html flexbox-intrinsic-ratio-001-ref.html
 == flexbox-intrinsic-ratio-002.html flexbox-intrinsic-ratio-001-ref.html
+== flexbox-intrinsic-ratio-002v.html flexbox-intrinsic-ratio-001-ref.html
 == flexbox-intrinsic-ratio-003.html flexbox-intrinsic-ratio-003-ref.html
+== flexbox-intrinsic-ratio-003v.html flexbox-intrinsic-ratio-003-ref.html
 == flexbox-intrinsic-ratio-004.html flexbox-intrinsic-ratio-004-ref.html
+== flexbox-intrinsic-ratio-004v.html flexbox-intrinsic-ratio-004-ref.html
 == flexbox-intrinsic-ratio-005.html flexbox-intrinsic-ratio-005-ref.html
+== flexbox-intrinsic-ratio-005v.html flexbox-intrinsic-ratio-005-ref.html
 == flexbox-intrinsic-ratio-006.html flexbox-intrinsic-ratio-006-ref.html
+== flexbox-intrinsic-ratio-006v.html flexbox-intrinsic-ratio-006-ref.html
 
 # Tests for flex items as (pseudo) stacking contexts
 == flexbox-items-as-stacking-contexts-001.xhtml flexbox-items-as-stacking-contexts-001-ref.xhtml
@@ -141,7 +151,9 @@
 == flexbox-mbp-horiz-001-rtl-reverse.xhtml flexbox-mbp-horiz-001-ref.xhtml
 == flexbox-mbp-horiz-002a.xhtml            flexbox-mbp-horiz-002-ref.xhtml
 == flexbox-mbp-horiz-002b.xhtml            flexbox-mbp-horiz-002-ref.xhtml
+== flexbox-mbp-horiz-002v.xhtml            flexbox-mbp-horiz-002-ref.xhtml
 == flexbox-mbp-horiz-003.xhtml             flexbox-mbp-horiz-003-ref.xhtml
+== flexbox-mbp-horiz-003v.xhtml            flexbox-mbp-horiz-003-ref.xhtml
 == flexbox-mbp-horiz-003-reverse.xhtml     flexbox-mbp-horiz-003-reverse-ref.xhtml
 == flexbox-mbp-horiz-004.xhtml             flexbox-mbp-horiz-004-ref.xhtml
 
@@ -199,7 +211,8 @@
 == flexbox-with-pseudo-elements-002.html flexbox-with-pseudo-elements-002-ref.html
 == flexbox-with-pseudo-elements-003.html flexbox-with-pseudo-elements-003-ref.html
 
-# Tests for combined influence of 'writing-mode' & 'direction' on flex axes
+# Tests for combined influence of 'writing-mode' & 'direction'
+# on flex container axes & flex item placement
 == flexbox-writing-mode-001.html flexbox-writing-mode-001-ref.html
 == flexbox-writing-mode-002.html flexbox-writing-mode-002-ref.html
 == flexbox-writing-mode-003.html flexbox-writing-mode-003-ref.html
@@ -209,6 +222,12 @@
 == flexbox-writing-mode-007.html flexbox-writing-mode-007-ref.html
 == flexbox-writing-mode-008.html flexbox-writing-mode-008-ref.html
 == flexbox-writing-mode-009.html flexbox-writing-mode-009-ref.html
+== flexbox-writing-mode-010.html flexbox-writing-mode-010-ref.html
+== flexbox-writing-mode-011.html flexbox-writing-mode-011-ref.html
+== flexbox-writing-mode-012.html flexbox-writing-mode-012-ref.html
+== flexbox-writing-mode-013.html flexbox-writing-mode-013-ref.html
+== flexbox-writing-mode-014.html flexbox-writing-mode-014-ref.html
+== flexbox-writing-mode-015.html flexbox-writing-mode-015-ref.html
 
 # Single-line size clamping
 == flexbox-single-line-clamp-1.html flexbox-single-line-clamp-1-ref.html
diff --git a/custom-elements/Document-createElement.html b/custom-elements/Document-createElement.html
index 97a5659..21f024c 100644
--- a/custom-elements/Document-createElement.html
+++ b/custom-elements/Document-createElement.html
@@ -36,12 +36,18 @@
     customElements.define('autonomous-custom-element', AutonomousCustomElement);
     customElements.define('is-custom-element', IsCustomElement);
 
-    var instance = document.createElement('autonomous-custom-element', { is: "is-custom-element"});
+    var instance = document.createElement('autonomous-custom-element', { is: 'is-custom-element'});
 
     assert_true(instance instanceof AutonomousCustomElement);
     assert_equals(instance.localName, 'autonomous-custom-element');
     assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
 
+    var instance2 = document.createElement('undefined-element', { is: 'is-custom-element'});
+    assert_false(instance2.matches(':defined'));
+    class DefinedLater extends HTMLElement {}
+    customElements.define('undefined-element', DefinedLater);
+    document.body.appendChild(instance2);
+    assert_true(instance2 instanceof DefinedLater);
 }, 'document.createElement must create an instance of autonomous custom elements when it has is attribute');
 
 function assert_reports(expected, testFunction, message) {
@@ -349,6 +355,7 @@
   // https://github.com/w3c/webcomponents/issues/608
   let div = document.createElement('div', { is: 'my-div' });
   assert_false(div instanceof MyElement);
+  assert_false(div.hasAttribute('is'));
 
   customElements.define('my-div', MyElement, { extends: 'div' });
   document.body.appendChild(div);
diff --git a/custom-elements/Document-createElementNS.html b/custom-elements/Document-createElementNS.html
index 5711a46..4cc4561 100644
--- a/custom-elements/Document-createElementNS.html
+++ b/custom-elements/Document-createElementNS.html
@@ -24,12 +24,21 @@
 }, 'autonomous: document.createElementNS should check namespaces.');
 
 test(() => {
+  const xhtmlNS = 'http://www.w3.org/1999/xhtml';
+  assert_false(document.createElementNS(xhtmlNS, 'x-foo') instanceof HTMLUnknownElement);
+  assert_false(document.createElementNS(xhtmlNS, 'x-foo', {}) instanceof HTMLUnknownElement);
+  assert_false((new Document()).createElementNS(xhtmlNS, 'x-foo') instanceof HTMLUnknownElement);
+  assert_false((new Document()).createElementNS(xhtmlNS, 'x-foo', {}) instanceof HTMLUnknownElement);
+}, 'autonomous: document.createElementNS should not create HTMLUnknownElement for a valid custom element name');
+
+test(() => {
   class MyBuiltinElement extends HTMLElement {};
 
   customElements.define('my-builtin', MyBuiltinElement, { extends: 'address' });
   let element = document.createElementNS('http://www.w3.org/1999/xhtml', 'p:address', { is: 'my-builtin'});
   assert_true(element instanceof MyBuiltinElement);
   assert_equals(element.prefix, 'p');
+  assert_false(element.hasAttribute('is'));
 }, 'builtin: document.createElementNS should create custom elements with prefixes.');
 
 test(() => {
@@ -38,6 +47,7 @@
   customElements.define('my-builtin2', MyBuiltinElement2, { extends: 'address'});
   let element = document.createElementNS('urn:example', 'address', { is: 'my-builtin2' });
   assert_false(element instanceof MyBuiltinElement2);
+  assert_false(element.hasAttribute('is'));
 }, 'builtin: document.createElementNS should check namespaces.');
 </script>
 </body>
diff --git a/custom-elements/builtin-coverage.html b/custom-elements/builtin-coverage.html
index 2778a68..bd084e9 100644
--- a/custom-elements/builtin-coverage.html
+++ b/custom-elements/builtin-coverage.html
@@ -1,9 +1,12 @@
 <!DOCTYPE html>
+<html is="my-html">
+<head>
 <meta charset="utf-8">
 <meta name="help" content="https://html.spec.whatwg.org/multipage/custom-elements.html#element-definition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<body>
+</head>
+<body is="my-body">
 <div id="container"></div>
 <script>
 class MyA extends HTMLAnchorElement {
@@ -667,15 +670,15 @@
   {tag: 'bdi', klass: MyBdi},
   {tag: 'bdo', klass: MyBdo},
   {tag: 'blockquote', klass: MyBlockquote},
-  {tag: 'body', klass: MyBody, innerHTML: 'skip'},
+  {tag: 'body', klass: MyBody, parsing: 'document'},
   {tag: 'br', klass: MyBr},
   {tag: 'button', klass: MyButton},
   {tag: 'canvas', klass: MyCanvas},
-  {tag: 'caption', klass: MyCaption, innerHTML: 'table'},
+  {tag: 'caption', klass: MyCaption, parsing: 'table'},
   {tag: 'cite', klass: MyCite},
   {tag: 'code', klass: MyCode},
-  {tag: 'col', klass: MyCol, innerHTML: 'table'},
-  {tag: 'colgroup', klass: MyColgroup, innerHTML: 'table'},
+  {tag: 'col', klass: MyCol, parsing: 'table'},
+  {tag: 'colgroup', klass: MyColgroup, parsing: 'table'},
   {tag: 'data', klass: MyData},
   {tag: 'dd', klass: MyDd},
   {tag: 'del', klass: MyDel},
@@ -700,7 +703,7 @@
   {tag: 'header', klass: MyHeader},
   {tag: 'hgroup', klass: MyHgroup},
   {tag: 'hr', klass: MyHr},
-  {tag: 'html', klass: MyHtml, innerHTML: 'skip'},
+  {tag: 'html', klass: MyHtml, parsing: 'document'},
   {tag: 'i', klass: MyI},
   {tag: 'iframe', klass: MyIframe},
   {tag: 'img', klass: MyImg},
@@ -747,16 +750,16 @@
   {tag: 'summary', klass: MySummary},
   {tag: 'sup', klass: MySup},
   {tag: 'table', klass: MyTable},
-  {tag: 'tbody', klass: MyTbody, innerHTML: 'table'},
-  {tag: 'td', klass: MyTd, innerHTML: 'table'},
+  {tag: 'tbody', klass: MyTbody, parsing: 'table'},
+  {tag: 'td', klass: MyTd, parsing: 'table'},
   {tag: 'template', klass: MyTemplate},
   {tag: 'textarea', klass: MyTextarea},
-  {tag: 'tfoot', klass: MyTfoot, innerHTML: 'table'},
-  {tag: 'th', klass: MyTh, innerHTML: 'table'},
-  {tag: 'thead', klass: MyThead, innerHTML: 'table'},
+  {tag: 'tfoot', klass: MyTfoot, parsing: 'table'},
+  {tag: 'th', klass: MyTh, parsing: 'table'},
+  {tag: 'thead', klass: MyThead, parsing: 'table'},
   {tag: 'time', klass: MyTime},
   {tag: 'title', klass: MyTitle},
-  {tag: 'tr', klass: MyTr, innerHTML: 'table'},
+  {tag: 'tr', klass: MyTr, parsing: 'table'},
   {tag: 'track', klass: MyTrack},
   {tag: 'u', klass: MyU},
   {tag: 'ul', klass: MyUl},
@@ -789,7 +792,7 @@
   }});
 }
 
-for (t of testData) {
+for (const t of testData) {
   test(() => {
     let name = 'my-' + t.tag;
     customElements.define(name, t.klass, { extends: t.tag });
@@ -797,27 +800,42 @@
     test(() => {
       let customized = new t.klass();
       assert_equals(customized.constructor, t.klass);
+      assert_equals(customized.cloneNode().constructor, t.klass,
+                    'Cloning a customized built-in element should succeed.');
     }, `${t.tag}: Operator 'new' should instantiate a customized built-in element`);
 
     test(() => {
       let customized = document.createElement(t.tag, { is: name });
       assert_equals(customized.constructor, t.klass);
+      assert_equals(customized.cloneNode().constructor, t.klass,
+                    'Cloning a customized built-in element should succeed.');
     }, `${t.tag}: document.createElement() should instantiate a customized built-in element`);
 
-    if (t.innerHTML == 'skip')
+    if (t.parsing == 'document') {
+      let test = async_test(`${t.tag}: document parser should instantiate a customized built-in element`);
+      window.addEventListener('load', test.step_func_done(() => {
+        let customized = document.querySelector(t.tag);
+        assert_equals(customized.constructor, t.klass);
+        assert_equals(customized.cloneNode().constructor, t.klass,
+                      'Cloning a customized built-in element should succeed.');
+      }));
       return;
+    }
     test(() => {
       let container = document.getElementById('container');
-      if (t.innerHTML == 'table') {
+      if (t.parsing == 'table') {
         container.innerHTML = `<table><${t.tag} is="${name}" id="${name}">`;
       } else {
         container.innerHTML = `<${t.tag} is="${name}" id="${name}">`;
       }
       let customized = document.getElementById(name);
       assert_equals(customized.constructor, t.klass);
+      assert_equals(customized.cloneNode().constructor, t.klass,
+                    'Cloning a customized built-in element should succeed.');
     }, `${t.tag}: innerHTML should instantiate a customized built-in element`);
 
   }, `${t.tag}: Define a customized built-in element`);
 }
 </script>
 </body>
+</html>
diff --git a/custom-elements/parser/serializing-html-fragments.html b/custom-elements/parser/serializing-html-fragments.html
new file mode 100644
index 0000000..6992dd6
--- /dev/null
+++ b/custom-elements/parser/serializing-html-fragments.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="container"></div>
+<script>
+test(() => {
+  class MyParagraph extends HTMLParagraphElement {}
+  customElements.define('my-p', MyParagraph, { extends: 'p' });
+
+  let p = new MyParagraph();
+  p.setAttribute('class', 'foo');
+  assert_equals(p.outerHTML, '<p is="my-p" class="foo"></p>');
+
+  let container = document.querySelector('#container');
+  container.appendChild(p);
+  container.innerHTML = container.innerHTML;
+  assert_not_equals(container.firstChild, p);
+  assert_true(container.firstChild instanceof MyParagraph);
+}, '"is" value should be serialized if the custom element has no "is" content attribute');
+
+test(() => {
+  let p = document.createElement('p', { is: 'your-p' });
+  assert_equals(p.outerHTML, '<p is="your-p"></p>');
+}, '"is" value should be serialized even for an undefined element');
+
+test(() => {
+  class MyDiv extends HTMLDivElement {}
+  customElements.define('my-div', MyDiv, { extends: 'div' });
+
+  let div = document.createElement('div', { is: 'my-div' });
+  div.setAttribute('is', 'foo"bar\n');
+  assert_equals(div.outerHTML, '<div is="foo&quot;bar\n"></div>');
+}, '"is" content attribute should be serialized even if the element is a customized built-in element');
+</script>
+</body>
diff --git a/custom-elements/pseudo-class-defined.html b/custom-elements/pseudo-class-defined.html
new file mode 100644
index 0000000..30d5ac9
--- /dev/null
+++ b/custom-elements/pseudo-class-defined.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics-other.html#selector-defined">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+const testList = [
+  { tag_name: 'div', defined: true },
+  { tag_name: 'a-a', defined: false },
+  { tag_name: 'font-face', defined: true },
+  { tag_name: 'abbr', is: 'my-abbr', defined: false },
+];
+
+// Setup iframe to test the parser.
+const neither = 'rgb(255, 0, 0)';
+const defined = 'rgb(255, 165, 0)';
+const not_defined = 'rgb(0, 0, 255)';
+iframe.srcdoc = `<style>
+  * { color:${neither}; }
+  :defined { color:${defined}; }
+  :not(:defined) { color:${not_defined}; }
+</style>`
+  + testList.map(d => `<${d.tag_name}${d.is ? ' is=' + d.is : ''}></${d.tag_name}>`).join('');
+setup({ explicit_done: true });
+iframe.onload = () => {
+  const doc = iframe.contentDocument;
+  const doc_without_browsing_context = doc.implementation.createHTMLDocument();
+  for (const data of testList) {
+    // Test elements inserted by parser.
+    test_defined(data.defined, doc.getElementsByTagName(data.tag_name)[0],
+                 `<${data.tag_name}${data.is ? ' is=' + data.is : ''}>`);
+
+    // Test DOM createElement() methods.
+    test_defined_for_createElement(data.defined, !data.defined, doc, data.tag_name, data.is);
+
+    // Documents without browsing context should behave the same.
+    test_defined_for_createElement(data.defined, false, doc_without_browsing_context, data.tag_name, data.is, 'Without browsing context: ');
+  }
+
+  done();
+};
+
+function test_defined_for_createElement(defined, should_test_change, doc, tag_name, is, description = '') {
+  let is_desc = is ? `, { is: "${is}" }` : '';
+  // Test document.createElement().
+  let element = is ? doc.createElement(tag_name, { is: is }) : doc.createElement(tag_name);
+  doc.body.appendChild(element);
+  test_defined(defined, element, `${description}createElement("${tag_name}"${is_desc})`);
+
+  // Test document.createElementNS().
+  let html_element = is ? doc.createElementNS('http://www.w3.org/1999/xhtml', tag_name, { is: is })
+                        : doc.createElementNS('http://www.w3.org/1999/xhtml', tag_name);
+  doc.body.appendChild(html_element);
+  test_defined(defined, html_element, `${description}createElementNS("http://www.w3.org/1999/xhtml", "${tag_name}"${is_desc})`);
+
+  // If the element namespace is not HTML, it should be "uncustomized"; i.e., "defined".
+  let svg_element = is ? doc.createElementNS('http://www.w3.org/2000/svg', tag_name, { is: is })
+                       : doc.createElementNS('http://www.w3.org/2000/svg', tag_name);
+  doc.body.appendChild(svg_element);
+  test_defined(true, svg_element, `${description}createElementNS("http://www.w3.org/2000/svg", "${tag_name}"${is_desc})`);
+
+  // Test ":defined" changes when the custom element was defined.
+  if (should_test_change) {
+    let w = doc.defaultView;
+    assert_false(!w, 'defaultView required to test change');
+    if (is) {
+      w.customElements.define(is, class extends w.HTMLElement {}, { extends: tag_name });
+    } else {
+      w.customElements.define(tag_name, class extends w.HTMLElement {
+        constructor() { super(); }
+      });
+    }
+
+    test_defined(true, element, `Upgraded ${description}createElement("${tag_name}"${is_desc})`);
+    test_defined(true, html_element, `Upgraded ${description}createElementNS("http://www.w3.org/1999/xhtml", "${tag_name}"${is_desc})`);
+  }
+}
+
+function test_defined(expected, element, description) {
+  test(() => {
+    assert_equals(element.matches(':defined'), expected, 'matches(":defined")');
+    assert_equals(element.matches(':not(:defined)'), !expected, 'matches(":not(:defined")');
+    const view = element.ownerDocument.defaultView;
+    if (!view)
+      return;
+    const style = view.getComputedStyle(element);
+    assert_equals(style.color, expected ? defined : not_defined, 'getComputedStyle');
+  }, `${description} should ${expected ? 'be' : 'not be'} :defined`);
+}
+</script>
diff --git a/custom-elements/upgrading/Document-importNode.html b/custom-elements/upgrading/Document-importNode.html
new file mode 100644
index 0000000..b80f906
--- /dev/null
+++ b/custom-elements/upgrading/Document-importNode.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-document-importnode">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/custom-elements-helpers.js"></script>
+<body>
+<script>
+test_with_window((w, doc) => {
+  class MyElement extends HTMLElement {}
+  class MyElement2 extends w.HTMLElement {}
+  customElements.define('my-element', MyElement);
+  w.customElements.define('my-element', MyElement2);
+
+  let original = document.createElement('my-element');
+  assert_true(original instanceof MyElement);
+
+  let imported = doc.importNode(original);
+  assert_true(imported instanceof MyElement2);
+}, 'autonomous: document.importNode() should import custom elements successfully');
+
+test_with_window((w, doc) => {
+  class MyElement3 extends w.HTMLElement {}
+  w.customElements.define('my-element3', MyElement3);
+
+  let original = document.createElement('my-element3');
+  assert_equals(original.constructor, HTMLElement);
+
+  let imported = doc.importNode(original);
+  assert_true(imported instanceof MyElement3);
+}, 'autonomous: document.importNode() should import "undefined" custom elements successfully');
+
+test_with_window((w, doc) => {
+  class MyDiv extends HTMLDivElement {}
+  class MyDiv2 extends w.HTMLDivElement {}
+  customElements.define('my-div', MyDiv, { extends: 'div' });
+  w.customElements.define('my-div', MyDiv2, { extends: 'div' });
+
+  let original = document.createElement('div', { is: 'my-div' });
+  assert_true(original instanceof MyDiv);
+
+  let imported = doc.importNode(original);
+  assert_true(imported instanceof MyDiv2);
+}, 'built-in: document.importNode() should import custom elements successfully');
+
+test_with_window((w, doc) => {
+  class MyDiv2 extends w.HTMLDivElement {}
+  w.customElements.define('my-div2', MyDiv2, { extends: 'div' });
+
+  let original = document.createElement('div', { is: 'my-div2' });
+  assert_equals(original.constructor, HTMLDivElement);
+
+  let imported = doc.importNode(original);
+  assert_true(imported instanceof MyDiv2);
+}, 'built-in: document.importNode() should import "undefined" custom elements successfully');
+</script>
+</body>
diff --git a/custom-elements/upgrading/Node-cloneNode.html b/custom-elements/upgrading/Node-cloneNode.html
index 0492e1f..1a8786e 100644
--- a/custom-elements/upgrading/Node-cloneNode.html
+++ b/custom-elements/upgrading/Node-cloneNode.html
@@ -49,6 +49,35 @@
         'A cloned custom element must be an instance of the custom element');
 }, 'Node.prototype.cloneNode(false) must be able to clone as a autonomous custom element when it contains is attribute');
 
+test(function () {
+    class MyDiv1 extends HTMLDivElement {};
+    class MyDiv2 extends HTMLDivElement {};
+    class MyDiv3 extends HTMLDivElement {};
+    customElements.define('my-div1', MyDiv1, { extends: 'div' });
+    customElements.define('my-div2', MyDiv2, { extends: 'div' });
+
+    let instance = document.createElement('div', { is: 'my-div1'});
+    assert_true(instance instanceof MyDiv1);
+    instance.setAttribute('is', 'my-div2');
+    let clone = instance.cloneNode(false);
+    assert_not_equals(instance, clone);
+    assert_true(clone instanceof MyDiv1,
+        'A cloned custom element must be an instance of the custom element even with an inconsistent "is" attribute');
+
+    let instance3 = document.createElement('div', { is: 'my-div3'});
+    assert_false(instance3 instanceof MyDiv3);
+    instance3.setAttribute('is', 'my-div2');
+    let clone3 = instance3.cloneNode(false);
+    assert_not_equals(instance3, clone);
+    customElements.define('my-div3', MyDiv3, { extends: 'div' });
+    document.body.appendChild(instance3);
+    document.body.appendChild(clone3);
+    assert_true(instance3 instanceof MyDiv3,
+        'An undefined element must be upgraded even with an inconsistent "is" attribute');
+    assert_true(clone3 instanceof MyDiv3,
+        'A cloned undefined element must be upgraded even with an inconsistent "is" attribute');
+}, 'Node.prototype.cloneNode(false) must be able to clone as a customized built-in element when it has an inconsistent "is" attribute');
+
 test_with_window(function (contentWindow) {
     var contentDocument = contentWindow.document;
     class MyCustomElement extends contentWindow.HTMLElement {}
diff --git a/docs/introduction.md b/docs/introduction.md
index c718efd..8833d86 100644
--- a/docs/introduction.md
+++ b/docs/introduction.md
@@ -102,17 +102,22 @@
 and read the [Windows Notes](#windows-notes) section below.
 
 To get the tests running, you need to set up the test domains in your
-[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system). The
-following entries are required:
+[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system).
 
+The necessary content can be generated with `./wpt make-hosts-file`; on
+Windows, you will need to preceed the prior command with `python` or
+the path to the Python binary (`python wpt make-hosts-file`).
+
+For example, on most UNIX-like systems, you can setup the hosts file with:
+
+```bash
+./wpt make-hosts-file | sudo tee -a /etc/hosts
 ```
-127.0.0.1   web-platform.test
-127.0.0.1   www.web-platform.test
-127.0.0.1   www1.web-platform.test
-127.0.0.1   www2.web-platform.test
-127.0.0.1   xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1   xn--lve-6lad.web-platform.test
-0.0.0.0     nonexistent-origin.web-platform.test
+
+And on Windows (note this requires an Administrator privileged shell):
+
+```bash
+python wpt make-hosts-file >> %SystemRoot%\System32\drivers\etc\hosts
 ```
 
 If you are behind a proxy, you also need to make sure the domains above are
diff --git a/domparsing/XMLSerializer-serializeToString.html b/domparsing/XMLSerializer-serializeToString.html
index 23323b9..92cdb10 100644
--- a/domparsing/XMLSerializer-serializeToString.html
+++ b/domparsing/XMLSerializer-serializeToString.html
@@ -39,6 +39,15 @@
   var xmlString = (new XMLSerializer()).serializeToString(root);
   assert_equals(xmlString, '<root xmlns="urn:bar"><outer xmlns=""><inner>value1</inner></outer></root>');
 }, 'Check if there is no redundant empty namespace declaration.');
+
+test(function() {
+  var serializer = new XMLSerializer();
+  var root = createXmlDoc().documentElement;
+  root.firstChild.setAttribute('attr1', 'value1\tvalue2\r\n');
+  var xmlString = serializer.serializeToString(root);
+  assert_equals(xmlString, '<root><child1 attr1="value1&#9;value2&#xD;&#xA;">value1</child1></root>');
+}, 'check XMLSerializer.serializeToString escapes attribute values for roundtripping');
+
 </script>
  </body>
 </html>
diff --git a/fetch/api/abort/general.any.js b/fetch/api/abort/general.any.js
index e2c6e8d..eb59797 100644
--- a/fetch/api/abort/general.any.js
+++ b/fetch/api/abort/general.any.js
@@ -78,6 +78,7 @@
   assert_true(Boolean(request.signal), "Signal member is present & truthy");
   assert_equals(request.signal.constructor, AbortSignal);
   assert_not_equals(request.signal, signal, 'Request has a new signal, not a reference');
+  assert_true(request.signal.aborted, `Request's signal has aborted`);
 
   const fetchPromise = fetch(request);
 
diff --git a/fetch/api/redirect/redirect-empty-location-worker.html b/fetch/api/redirect/redirect-empty-location-worker.html
new file mode 100644
index 0000000..7dce98c
--- /dev/null
+++ b/fetch/api/redirect/redirect-empty-location-worker.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Fetch in worker: handling empty Location header during redirection</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#http-network-or-cache-fetch">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+      fetch_tests_from_worker(new Worker("redirect-empty-location.js"));
+    </script>
+  </body>
+</html>
diff --git a/fetch/api/redirect/redirect-empty-location.html b/fetch/api/redirect/redirect-empty-location.html
new file mode 100644
index 0000000..afb033b
--- /dev/null
+++ b/fetch/api/redirect/redirect-empty-location.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Fetch: handling empty Location header during redirection</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#http-network-or-cache-fetch">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script src="../resources/utils.js"></script>
+    <script src="redirect-empty-location.js"></script>
+  </body>
+</html>
diff --git a/fetch/api/redirect/redirect-empty-location.js b/fetch/api/redirect/redirect-empty-location.js
new file mode 100644
index 0000000..a9d03c5
--- /dev/null
+++ b/fetch/api/redirect/redirect-empty-location.js
@@ -0,0 +1,23 @@
+// Tests receiving a redirect response with a Location header with an empty
+// value.
+
+if (this.document === undefined) {
+  importScripts("/resources/testharness.js");
+  importScripts("../resources/utils.js");
+}
+
+const url = RESOURCES_DIR + 'redirect-empty-location.py';
+
+promise_test(t => {
+  return promise_rejects(t, new TypeError(), fetch(url, {redirect:'follow'}));
+}, 'redirect response with empty Location, follow mode');
+
+promise_test(t => {
+  return fetch(url, {redirect:'manual'})
+    .then(resp => {
+      assert_equals(resp.type, 'opaqueredirect');
+      assert_equals(resp.status, 0);
+    });
+}, 'redirect response with empty Location, manual mode');
+
+done();
diff --git a/fetch/api/resources/redirect-empty-location.py b/fetch/api/resources/redirect-empty-location.py
new file mode 100644
index 0000000..2baabae
--- /dev/null
+++ b/fetch/api/resources/redirect-empty-location.py
@@ -0,0 +1,3 @@
+def main(request, response):
+    headers = [("Location", "")]
+    return 302, headers, ""
diff --git a/fetch/corb/README.md b/fetch/corb/README.md
new file mode 100644
index 0000000..00e8c16
--- /dev/null
+++ b/fetch/corb/README.md
@@ -0,0 +1,21 @@
+# Tests related to Cross-Origin Resource Blocking (CORB).
+
+This directory contains tests related to the
+[Cross-Origin Resource Blocking (CORB)](https://chromium.googlesource.com/chromium/src/+/master/content/browser/loader/cross_origin_read_blocking_explainer.md) algorithm.
+
+Note that CORB is currently in very early stages of standardization path.  At
+the same time, some tests in this directory (e.g.
+`css-with-json-parser-breaker`) cover behavior spec-ed outside of CORB (making
+sure that CORB doesn't change the existing web behavior) and therefore are
+valuable independently from CORB's standardization efforts.
+
+Tests that cover behavior that is changed by CORB have to be marked as
+[tentative](http://web-platform-tests.org/writing-tests/file-names.html)
+(using `.tentative` substring in their filename) until CORB
+is included in the official
+[Fetch spec](https://fetch.spec.whatwg.org/).
+
+The tests in this directory interact with various, random features,
+but the tests have been grouped together into the `fetch/corb` directory,
+because all of these tests verify behavior that is important to the CORB
+algorithm.
diff --git a/fetch/corb/css-with-json-parser-breaker.sub.html b/fetch/corb/css-with-json-parser-breaker.sub.html
new file mode 100644
index 0000000..9d930f9
--- /dev/null
+++ b/fetch/corb/css-with-json-parser-breaker.sub.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CORB should not block text/css with a JSON parser breaker</title>
+<link rel="stylesheet" type="text/css"
+      href="http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/css-with-json-parser-breaker.css">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+  <h1 id="header">Header example</h1>
+  <p>Paragraph body</p>
+</body>
+<script>
+test(function() {
+  var style = getComputedStyle(document.getElementById('header'));
+  assert_equals(style.getPropertyValue('color'), 'rgb(255, 0, 0)');
+}, "CORB should not block text/css with a JSON parser breaker");
+</script>
diff --git a/fetch/corb/resources/css-with-json-parser-breaker.css b/fetch/corb/resources/css-with-json-parser-breaker.css
new file mode 100644
index 0000000..3ba3365
--- /dev/null
+++ b/fetch/corb/resources/css-with-json-parser-breaker.css
@@ -0,0 +1,3 @@
+)]}'
+{}
+h1 { color: red; }
diff --git a/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
index 2820b75..e0b4b0c 100644
--- a/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -11,6 +11,7 @@
 <div id=log></div>
 <iframe id="B"></iframe>
 <iframe id="C"></iframe>
+<iframe id="D"></iframe>
 <script>
 
 /*
@@ -21,10 +22,13 @@
 
 setup({explicit_done: true});
 path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame.html';
+pathWithThen = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame-with-then.html';
 var B = document.getElementById('B').contentWindow;
 var C = document.getElementById('C').contentWindow;
+var D = document.getElementById('D').contentWindow;
 B.frameElement.uriToLoad = path;
 C.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + path;
+D.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + pathWithThen;
 
 function reloadSubframes(cb) {
   var iframes = document.getElementsByTagName('iframe');
@@ -45,8 +49,8 @@
  */
 
 var testList = [];
-function addTest(fun, desc) { testList.push([fun, desc]); }
-
+function addTest(func, desc) { testList.push({func, desc, promiseTest: false}); }
+function addPromiseTest(func, desc) { testList.push({func, desc, promiseTest: true}); }
 
 /*
  * Basic sanity testing.
@@ -72,10 +76,10 @@
 
 var whitelistedWindowIndices = ['0', '1'];
 var whitelistedWindowPropNames = ['location', 'postMessage', 'window', 'frames', 'self', 'top', 'parent',
-                                  'opener', 'closed', 'close', 'blur', 'focus', 'length'];
+                                  'opener', 'closed', 'close', 'blur', 'focus', 'length', 'then'];
 whitelistedWindowPropNames = whitelistedWindowPropNames.concat(whitelistedWindowIndices);
 whitelistedWindowPropNames.sort();
-var whitelistedLocationPropNames = ['href', 'replace'];
+var whitelistedLocationPropNames = ['href', 'replace', 'then'];
 whitelistedLocationPropNames.sort();
 var whitelistedSymbols = [Symbol.toStringTag, Symbol.hasInstance,
                           Symbol.isConcatSpreadable];
@@ -191,7 +195,7 @@
   assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
   if (!isArrayIndexPropertyName) {
     assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should not be enumerable");
-    if(isSymbol) {
+    if (isSymbol || propName == "then") {
       assert_true("value" in desc,
                   "property descriptor for " + propName + " should be a value descriptor");
       assert_equals(desc.value, undefined,
@@ -222,6 +226,10 @@
   });
 }, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should be set up correctly");
 
+addTest(function() {
+  assert_equals(typeof D.then, "object");
+}, "[[GetOwnProperty]] - Subframe named 'then' should shadow the default 'then' value");
+
 /*
  * [[Delete]]
  */
@@ -309,6 +317,10 @@
   indexedWindowProps = allWindowProps.slice(0, whitelistedWindowIndices.length);
   stringWindowProps = allWindowProps.slice(0, -1 * whitelistedSymbols.length);
   symbolWindowProps = allWindowProps.slice(-1 * whitelistedSymbols.length);
+  // stringWindowProps should have "then" last in this case.  Do this
+  // check before we call stringWindowProps.sort() below.
+  assert_equals(stringWindowProps[stringWindowProps.length - 1], "then",
+                "'then' property should be added to the end of the string list if not there");
   assert_array_equals(indexedWindowProps, whitelistedWindowIndices,
                       "Reflect.ownKeys should start with the indices exposed on the cross-origin window.");
   assert_array_equals(stringWindowProps.sort(), whitelistedWindowPropNames,
@@ -326,6 +338,17 @@
 }, "[[OwnPropertyKeys]] should place the symbols after the property names after the subframe indices");
 
 addTest(function() {
+  var stringProps = Object.getOwnPropertyNames(D);
+  // Named frames are not exposed via [[OwnPropertyKeys]].
+  assert_equals(stringProps.indexOf("a"), -1);
+  assert_equals(stringProps.indexOf("b"), -1);
+  assert_equals(typeof D.a, "object");
+  assert_equals(typeof D.b, "object");
+  assert_equals(stringProps[stringProps.length - 1], "then");
+  assert_equals(stringProps.indexOf("then"), stringProps.lastIndexOf("then"));
+}, "[[OwnPropertyKeys]] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then'");
+
+addTest(function() {
   assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C's Window");
   assert_true(B.eval('parent.C.location') === C.location, "A and B observe the same identity for C's Location");
 }, "A and B jointly observe the same identity for cross-origin Window and Location");
@@ -393,16 +416,43 @@
   assert_equals({}.toString.call(C.location), "[object Object]");
 }, "{}.toString.call() does the right thing on cross-origin objects");
 
+addPromiseTest(function() {
+  return Promise.resolve(C).then((arg) => {
+    assert_equals(arg, C);
+  });
+}, "Resolving a promise with a cross-origin window without a 'then' subframe should work.");
+
+addPromiseTest(function() {
+  return Promise.resolve(D).then((arg) => {
+    assert_equals(arg, D);
+  });
+}, "Resolving a promise with a cross-origin window with a 'then' subframe should work.");
+
+addPromiseTest(function() {
+  return Promise.resolve(D.location).then((arg) => {
+    assert_equals(arg, D.location);
+  });
+}, "Resolving a promise with a cross-origin location should work.");
+
 // We do a fresh load of the subframes for each test to minimize side-effects.
 // It would be nice to reload ourselves as well, but we can't do that without
 // disrupting the test harness.
+function testDone() {
+  if (testList.length != 0) {
+    reloadSubframes(runNextTest);
+  } else {
+    done();
+  }
+}
+
 function runNextTest() {
   var entry = testList.shift();
-  test(() => entry[0](), entry[1])
-  if (testList.length != 0)
-    reloadSubframes(runNextTest);
-  else
-    done();
+  if (entry.promiseTest) {
+    promise_test(() => entry.func().finally(testDone), entry.desc);
+  } else {
+    test(entry.func, entry.desc);
+    testDone();
+  }
 }
 reloadSubframes(runNextTest);
 
diff --git a/html/browsers/origin/cross-origin-objects/frame-with-then.html b/html/browsers/origin/cross-origin-objects/frame-with-then.html
new file mode 100644
index 0000000..96cdf1e
--- /dev/null
+++ b/html/browsers/origin/cross-origin-objects/frame-with-then.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+  <body>
+    <!--- Some frames to test ordering -->
+    <iframe name="a"></iframe>
+    <!-- A subframe to test "then" behavior -->
+    <iframe name="then"></iframe>
+    <iframe name="b"></iframe>
+  </body>
+</html>
diff --git a/html/browsers/origin/cross-origin-objects/frame.html b/html/browsers/origin/cross-origin-objects/frame.html
index 341da6a..0d81624 100644
--- a/html/browsers/origin/cross-origin-objects/frame.html
+++ b/html/browsers/origin/cross-origin-objects/frame.html
@@ -6,6 +6,10 @@
   // properly ignored cross-origin.
   window.frames = "override";
 
+  // Also add a |then| property to test that it doesn't get exposed.
+  window.then = "something";
+  window.location.then = "something-else";
+
   // If we get a postMessage, we grab references to everything and set
   // document.domain to trim off our topmost subdomain.
   window.onmessage = function(evt) {
diff --git a/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js b/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js
new file mode 100644
index 0000000..6d540ce
--- /dev/null
+++ b/html/browsers/windows/auxiliary-browsing-contexts/opener-setter.window.js
@@ -0,0 +1,38 @@
+[
+  undefined,
+  42,
+  function() { return "hi" },
+  "hi",
+  {},
+  [],
+  Symbol()
+].forEach(val => {
+  test(t => {
+    const frame = document.body.appendChild(document.createElement("iframe")),
+          win = frame.contentWindow;
+    t.add_cleanup(() => frame.remove());
+
+    assert_own_property(win, "opener");
+    assert_equals(win.opener, null);
+    const beforeDesc = Object.getOwnPropertyDescriptor(win, "opener"),
+          openerGet = beforeDesc.get,
+          openerSet = beforeDesc.set;
+    assert_own_property(beforeDesc, "get");
+    assert_own_property(beforeDesc, "set");
+    assert_true(beforeDesc.enumerable);
+    assert_true(beforeDesc.configurable);
+
+    win.opener = val;
+    assert_equals(win.opener, val);
+    assert_equals(openerGet(), null);
+
+    const desc = Object.getOwnPropertyDescriptor(win, "opener");
+    assert_equals(desc.value, val);
+    assert_true(desc.writable);
+    assert_true(desc.enumerable);
+    assert_true(desc.configurable);
+
+    openerSet("x");
+    assert_equals(win.opener, "x");
+  }, "Setting window.opener to " + String(val)); // String() needed for symbols
+});
diff --git a/html/dom/documents/dom-tree-accessors/Document.body.html b/html/dom/documents/dom-tree-accessors/Document.body.html
index 7d85488..f421250 100644
--- a/html/dom/documents/dom-tree-accessors/Document.body.html
+++ b/html/dom/documents/dom-tree-accessors/Document.body.html
@@ -216,4 +216,12 @@
   assert_equals(f2.nextSibling, f1, "New frameset should have replaced the body");
 }, "Setting document.body to a frameset will replace the first existing body/frameset.");
 
+test(function() {
+  var doc = createDocument();
+  doc.appendChild(doc.createElement("test"));
+  var new_body = doc.createElement("body");
+  doc.body = new_body;
+  assert_equals(doc.documentElement.firstChild, new_body, "new_body should be inserted");
+  assert_equals(doc.body, null, "Getter should return null when the root is not html");
+}, "Setting document.body to a new body element when the root element is a test element.");
 </script>
diff --git a/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html b/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html
new file mode 100644
index 0000000..42da515
--- /dev/null
+++ b/html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>contentEditable inherit from light tree parent</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#contenteditable">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>You should see the word PASS two times below and no FAIL.</p>
+<div id="host1" contenteditable><div>FAILPASS</div></div>
+<div id="host2" contenteditable><div>FAILPASS</div></div>
+<script>
+  test(() => {
+    const root = host1.attachShadow({mode:"open"});
+    root.innerHTML = "<slot></slot>";
+    const text = host1.firstChild.firstChild;
+    const selection = window.getSelection();
+    selection.collapse(text, 0);
+    selection.extend(text, 4);
+    host1.focus();
+    document.execCommand("delete");
+    host1.blur();
+    assert_equals(text.data, "PASS", "Text should be PASS after FAIL is deleted");
+  }, "Slotted child of contenteditable host should be editable - slot direct child of shadow root");
+
+  test(() => {
+    const root = host2.attachShadow({mode:"open"});
+    root.innerHTML = "<div><slot></slot></div>";
+    const text = host2.firstChild.firstChild;
+    const selection = window.getSelection();
+    selection.collapse(text, 0);
+    selection.extend(text, 4);
+    host2.focus();
+    document.execCommand("delete");
+    host2.blur();
+    assert_equals(text.data, "PASS", "Text should be PASS after FAIL is deleted");
+  }, "Slotted child of contenteditable host should be editable - slot wrapped in shadow tree ancestor");
+</script>
diff --git a/html/infrastructure/common-dom-interfaces/collections/historical.html b/html/infrastructure/common-dom-interfaces/collections/historical.html
index a26dd4c..ef8345a 100644
--- a/html/infrastructure/common-dom-interfaces/collections/historical.html
+++ b/html/infrastructure/common-dom-interfaces/collections/historical.html
@@ -5,6 +5,8 @@
 <div id=log></div>
 <form id=form><input name=foo></form>
 <select id=select><option name=bar></select>
+<div id=dupe>
+<div id=dupe>
 <script>
 test(function() {
   var collection = document.getElementById('form').elements;
@@ -22,4 +24,10 @@
   });
 }, 'HTMLOptionsCollection legacycaller should not be supported');
 
+test(function() {
+  var collection = document.all('dupe', 0);
+  // If the second argument were used, it would return the first item of the
+  // collection instead of the whole collection.
+  assert_equals(collection.length, 2, 'length');
+}, 'HTMLAllCollection legacycaller with two arguments should not be supported');
 </script>
diff --git a/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html b/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
index e17c0eb..d425996 100644
--- a/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
+++ b/html/infrastructure/common-dom-interfaces/collections/htmlallcollection.html
@@ -22,6 +22,7 @@
 <div id="undefined"></div>
 <div id="null"></div>
 <div name="divwithname"></div>
+<div id="-0"></div>
 <script>
 var anchors = document.querySelectorAll("a");
 var divs = document.querySelectorAll("div");
@@ -33,19 +34,20 @@
 }, "document.all is an HTMLAllCollection");
 
 test(function() {
-  assert_equals(document.all.length, 23);
+  assert_equals(document.all.length, 24);
 }, "length attribute");
 
 // indexed property getter
 
 test(function() {
   assert_equals(document.all[0], document.documentElement);
-  assert_equals(document.all[22], scripts[2]);
+  assert_equals(document.all[-0], document.documentElement);
+  assert_equals(document.all[23], scripts[2]);
 }, "indexed property getter");
 
 test(function() {
   assert_equals(document.all[-1], undefined);
-  assert_equals(document.all[23], undefined);
+  assert_equals(document.all[24], undefined);
   assert_equals(document.all[42], undefined);
   assert_equals(document.all[43], undefined);
   assert_equals(document.all[4294967294], undefined);
@@ -84,8 +86,8 @@
 
 test(function() {
   assert_equals(document.all["0"], document.documentElement);
-  assert_equals(document.all["22"], document.scripts[2]);
-  assert_equals(document.all["23"], undefined);
+  assert_equals(document.all["23"], document.scripts[2]);
+  assert_equals(document.all["24"], undefined);
   assert_equals(document.all["42"], undefined);
   assert_equals(document.all["43"], undefined);
 }, "named property getter with \"array index property name\"");
@@ -97,6 +99,7 @@
   assert_equals(document.all["4294967294"], undefined);
   assert_equals(document.all["4294967295"], divs[1]);
   assert_equals(document.all["4294967296"], divs[2]);
+  assert_equals(document.all["-0"], divs[6]);
 }, "named property getter with invalid \"array index property name\"");
 
 test(function() {
@@ -130,8 +133,8 @@
 
 test(function() {
   assert_equals(document.all.namedItem("0"), null);
-  assert_equals(document.all.namedItem("22"), null);
   assert_equals(document.all.namedItem("23"), null);
+  assert_equals(document.all.namedItem("24"), null);
   assert_equals(document.all.namedItem("42"), spans[0]);
   assert_equals(document.all.namedItem("43"), null);
 }, "namedItem method with \"array index property name\"");
@@ -143,6 +146,7 @@
   assert_equals(document.all.namedItem("4294967294"), divs[0]);
   assert_equals(document.all.namedItem("4294967295"), divs[1]);
   assert_equals(document.all.namedItem("4294967296"), divs[2]);
+  assert_equals(document.all.namedItem("-0"), divs[6]);
 }, "namedItem method with invalid \"array index property name\"");
 
 test(function() {
@@ -183,16 +187,16 @@
 
 test(function() {
   assert_equals(document.all("0"), document.documentElement);
-  assert_equals(document.all("22"), document.scripts[2]);
-  assert_equals(document.all("23"), null);
+  assert_equals(document.all("23"), document.scripts[2]);
+  assert_equals(document.all("24"), null);
   assert_equals(document.all("42"), null);
   assert_equals(document.all("43"), null);
 }, "legacy caller with \"array index property name\"");
 
 test(function() {
   assert_equals(document.all(0), document.documentElement);
-  assert_equals(document.all(22), document.scripts[2]);
-  assert_equals(document.all(23), null);
+  assert_equals(document.all(23), document.scripts[2]);
+  assert_equals(document.all(24), null);
   assert_equals(document.all(42), null);
   assert_equals(document.all(43), null);
 }, "legacy caller with \"array index property name\" as number");
@@ -204,6 +208,7 @@
   assert_equals(document.all("4294967294"), null);
   assert_equals(document.all("4294967295"), divs[1]);
   assert_equals(document.all("4294967296"), divs[2]);
+  assert_equals(document.all("-0"), divs[6]);
 }, "legacy caller with invalid \"array index property name\"");
 
 test(function() {
@@ -262,16 +267,16 @@
 
 test(function() {
   assert_equals(document.all.item("0"), document.documentElement);
-  assert_equals(document.all.item("22"), document.scripts[2]);
-  assert_equals(document.all.item("23"), null);
+  assert_equals(document.all.item("23"), document.scripts[2]);
+  assert_equals(document.all.item("24"), null);
   assert_equals(document.all.item("42"), null);
   assert_equals(document.all.item("43"), null);
 }, "item method with \"array index property name\"");
 
 test(function() {
   assert_equals(document.all.item(0), document.documentElement);
-  assert_equals(document.all.item(22), document.scripts[2]);
-  assert_equals(document.all.item(23), null);
+  assert_equals(document.all.item(23), document.scripts[2]);
+  assert_equals(document.all.item(24), null);
   assert_equals(document.all.item(42), null);
   assert_equals(document.all.item(43), null);
 }, "item method with \"array index property name\" as number");
@@ -283,6 +288,7 @@
   assert_equals(document.all.item("4294967294"), null);
   assert_equals(document.all.item("4294967295"), divs[1]);
   assert_equals(document.all.item("4294967296"), divs[2]);
+  assert_equals(document.all.item("-0"), divs[6]);
 }, "item method with invalid \"array index property name\"");
 
 test(function() {
diff --git a/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html b/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html
index 38364cd..e6f0c2b 100644
--- a/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html
+++ b/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html
@@ -7,47 +7,89 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <script>
+function setColorAttributes(doc, color) {
+  doc.fgColor = color;
+  doc.bgColor = color;
+  doc.linkColor = color;
+  doc.vlinkColor = color;
+  doc.alinkColor = color;
+}
+
+function checkColorAttributes(doc, expected) {
+  assert_equals(document.fgColor, expected);
+  assert_equals(document.bgColor, expected);
+  assert_equals(document.linkColor, expected);
+  assert_equals(document.vlinkColor, expected);
+  assert_equals(document.alinkColor, expected);
+}
+
 test(function() {
-  document.fgColor = "green";
-  document.bgColor = "green";
-  document.linkColor = "green";
-  document.vlinkColor = "green";
-  document.alinkColor = "green";
+  setColorAttributes(document, 'green');
 
   var body = document.documentElement.removeChild(document.body);
-
+  this.add_cleanup(function() {
+    // Re-add body and reset color attributes.
+    document.body = body;
+    setColorAttributes(document, '');
+  });
   // When there is no body element, the color attributes return an
   // empty string upon getting.
-  assert_equals(document.fgColor, "");
-  assert_equals(document.bgColor, "");
-  assert_equals(document.linkColor, "");
-  assert_equals(document.vlinkColor, "");
-  assert_equals(document.alinkColor, "");
-
-  // Re-add body and reset color attributes.
-  document.body = body;
-  document.fgColor = "";
-  document.bgColor = "";
-  document.linkColor = "";
-  document.vlinkColor = "";
-  document.alinkColor = "";
+  checkColorAttributes(document, '');
 }, "getting document color attributes with no body");
 
 test(function() {
   var body = document.documentElement.removeChild(document.body);
+  this.add_cleanup(function() {
+    document.body = body;
+  });
 
   // When there is no body element, setting the color attributes has no effect.
-  document.fgColor = "red";
-  document.bgColor = "red";
-  document.linkColor = "red";
-  document.vlinkColor = "red";
-  document.alinkColor = "red";
-  assert_equals(document.fgColor, "");
-  assert_equals(document.bgColor, "");
-  assert_equals(document.linkColor, "");
-  assert_equals(document.vlinkColor, "");
-  assert_equals(document.alinkColor, "");
-
-  document.body = body;
+  setColorAttributes(document, 'red');
+  checkColorAttributes(document, '');
 }, "setting document color attributes with no body");
+
+function testBogusRootElement(doc) {
+  doc.replaceChild(doc.createElement('test'), doc.documentElement);
+  var new_body = doc.createElement('body');
+  doc.documentElement.appendChild(new_body);
+
+  setColorAttributes(doc, 'red');
+
+  assert_equals(new_body.attributes.length, 0, 'new_body.attributes.length');
+  checkColorAttributes(doc, '');
+}
+
+function createIframeDoc(markup) {
+  var iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  var doc = iframe.contentDocument;
+  doc.open();
+  doc.write(markup);
+  doc.close();
+  return doc;
+}
+
+test(function() {
+  // Use standards mode for doc
+  var doc = createIframeDoc('<!doctype html>');
+  testBogusRootElement(doc);
+}, "document color attributes when the root element is a test element (iframe)");
+
+test(function() {
+  var doc = document.implementation.createHTMLDocument();
+  testBogusRootElement(doc);
+}, "document color attributes when the root element is a test element (createHTMLDocument)");
+
+test(function() {
+  var doc = createIframeDoc('<!doctype html><frameset text=red link=red vlink=red alink=red bgcolor=red>');
+  assert_equals(doc.body.attributes.length, 5, 'attributes.length on the frameset');
+  checkColorAttributes(doc, '');
+}, "getting document color attributes when document.body is a frameset");
+
+test(function() {
+  var doc = createIframeDoc('<!doctype html><frameset>');
+  setColorAttributes(doc, 'red');
+  assert_equals(doc.body.attributes.length, 0, 'attributes.length on the frameset');
+  checkColorAttributes(doc, '');
+}, "setting document color attributes when document.body is a frameset");
 </script>
diff --git a/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html b/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html
new file mode 100644
index 0000000..27c76b6
--- /dev/null
+++ b/html/semantics/embedded-content/media-elements/track/track-element/src-empty-string.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Setting HTMLTrackElement.src to the empty string fires 'error' and sets readyState to ERROR</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/media.html#sourcing-out-of-band-text-tracks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<video></video>
+<script>
+async_test(t => {
+  let track = document.createElement("track");
+  track.src = '';
+  track.default = true;
+  track.onerror = t.step_func_done(() => {
+    assert_equals(track.readyState, HTMLTrackElement.ERROR);
+  });
+  track.onload = t.unreached_func('fired load');
+
+  assert_equals(track.readyState, HTMLTrackElement.NONE);
+
+  document.querySelector('video').appendChild(track);
+});
+</script>
diff --git a/html/semantics/forms/textfieldselection/defaultSelection.html b/html/semantics/forms/textfieldselection/defaultSelection.html
new file mode 100644
index 0000000..eb9bb40
--- /dev/null
+++ b/html/semantics/forms/textfieldselection/defaultSelection.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea>g</textarea>
+<input type="text" value="foo">
+</input>
+<script>
+test(function() {
+    let textarea = document.querySelector('textarea');
+    assert_equals(textarea.selectionStart, 0);
+    assert_equals(textarea.selectionEnd, 0);
+}, "Default selectionStart and selectionEnd for textarea");
+
+test(function() {
+    let textarea = document.querySelector('input');
+    assert_equals(textarea.selectionStart, 0);
+    assert_equals(textarea.selectionEnd, 0);
+}, "Default selectionStart and selectionEnd for input");
+
+test(function() {
+    let textarea = document.querySelector('textarea');
+    textarea.value="g";
+    assert_equals(textarea.selectionStart, 0);
+    assert_equals(textarea.selectionEnd, 0);
+}, "selectionStart and selectionEnd do not change when same value set again");
+
+test(function() {
+    let textarea = document.querySelector('textarea');
+    textarea.value="G";
+    assert_equals(textarea.selectionStart, 1);
+    assert_equals(textarea.selectionEnd, 1);
+}, "selectionStart and selectionEnd change when value changed to upper case");
+</script>
diff --git a/html/semantics/forms/textfieldselection/setSelectionRange.html b/html/semantics/forms/textfieldselection/setSelectionRange.html
new file mode 100644
index 0000000..bdf52a7
--- /dev/null
+++ b/html/semantics/forms/textfieldselection/setSelectionRange.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<textarea>
+
+</textarea>
+<script>
+test(function() {
+    let textarea = document.querySelector('textarea');
+    assert_equals(textarea.selectionStart, 0);
+    assert_equals(textarea.selectionEnd, 0);
+    textarea.setSelectionRange(0, 1);
+    assert_equals(textarea.selectionStart, 0);
+    assert_equals(textarea.selectionEnd, 1);
+}, "setSelectionRange on line boundaries");
+</script>
diff --git a/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py b/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py
new file mode 100644
index 0000000..5df883c
--- /dev/null
+++ b/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py
@@ -0,0 +1,4 @@
+def main(request, response):
+    headers = [("Content-Type", "text/javascript"), ("Cache-control", "public, max-age=100")]
+    body = "throw('fox');"
+    return 200, headers, body
diff --git a/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html b/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html
new file mode 100644
index 0000000..37f4a87
--- /dev/null
+++ b/html/semantics/scripting-1/the-script-element/emptyish-script-elements.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Treatment of various empty-ish script elements</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#prepare-a-script">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#already-started">
+<link rel="help" href="https://github.com/whatwg/html/issues/3419">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script id="no-children"></script>
+<script id="whitespace-child"> </script>
+
+<script id="gets-a-no-text-child"></script>
+<script id="gets-an-empty-text-child"></script>
+<script id="gets-a-text-child"></script>
+<script id="gets-a-comment-child"></script>
+<script id="gets-a-text-descendant"></script>
+
+<script>
+"use strict";
+
+test(() => {
+  const el = document.querySelector("#no-children");
+  el.appendChild(document.createTextNode("window.noChildrenExecuted = true;"));
+  assert_true(window.noChildrenExecuted);
+}, "A script with no children bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+  const el = document.querySelector("#whitespace-child");
+  el.appendChild(document.createTextNode("window.whitespaceChildExecuted = true;"));
+  assert_equals(window.whitespaceChildExecuted, undefined);
+}, "A script with a whitespace child executes, setting already-started, so adding text is a no-op");
+
+test(() => {
+  const el = document.querySelector("#gets-a-no-text-child");
+  el.appendChild(document.createElement("span"));
+  el.appendChild(document.createTextNode("window.getsANoTextChildExecuted = true;"));
+  assert_true(window.getsANoTextChildExecuted);
+}, "A script with an empty element inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+  const el = document.querySelector("#gets-an-empty-text-child");
+  el.appendChild(document.createTextNode(""));
+  el.appendChild(document.createTextNode("window.getsAnEmptyTextChildExecuted = true;"));
+  assert_true(window.getsAnEmptyTextChildExecuted);
+}, "A script with an empty text node inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+  const el = document.querySelector("#gets-a-text-child");
+  el.appendChild(document.createTextNode("window.getsATextChildExecuted1 = true;"));
+  el.appendChild(document.createTextNode("window.getsATextChildExecuted2 = true;"));
+  assert_true(window.getsATextChildExecuted1);
+  assert_equals(window.getsATextChildExecuted2, undefined);
+}, "A script with a text child inserted executes, setting already-started, so adding text is a no-op");
+
+test(() => {
+  const el = document.querySelector("#gets-a-comment-child");
+  el.appendChild(document.createComment("window.getsACommentChild1 = true;"));
+  el.appendChild(document.createTextNode("window.getsACommentChild2 = true;"));
+  assert_equals(window.getsACommentChild1, undefined);
+  assert_true(window.getsACommentChild2);
+}, "A script with a comment child inserted bails early, before setting already-started, so can be executed when adding text");
+
+test(() => {
+  const el = document.querySelector("#gets-a-text-descendant");
+  const child = document.createElement("span");
+  child.appendChild(document.createTextNode("window.getsATextDescendantExecuted1 = true;"));
+  el.appendChild(child);
+  el.appendChild(document.createTextNode("window.getsATextDescendantExecuted2 = true;"));
+  assert_equals(window.getsATextDescendantExecuted1, undefined);
+  assert_true(window.getsATextDescendantExecuted2);
+}, "A script with an element containing text inserted bails early, before setting already-started, so can be executed when adding text");
+</script>
diff --git a/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html b/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html
new file mode 100644
index 0000000..255e79e
--- /dev/null
+++ b/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="cacheable-script-throw.py?iframe"></script>
diff --git a/html/semantics/scripting-1/the-script-element/muted-errors.sub.html b/html/semantics/scripting-1/the-script-element/muted-errors.sub.html
new file mode 100644
index 0000000..a42179d
--- /dev/null
+++ b/html/semantics/scripting-1/the-script-element/muted-errors.sub.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Muted Errors</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// https://html.spec.whatwg.org/#report-the-error
+// If script's muted errors is true, then set message to "Script error.",
+// urlString to the empty string, line and col to 0, and errorValue to null.
+    setup({allow_uncaught_exception: true});
+
+    window.log = [];
+    window.addEventListener("error", ev => log.push(ev));
+
+    function check(shouldBeMuted) {
+      assert_equals(log.length, 1);
+      var ev = log[0];
+      log = [];
+      if (shouldBeMuted) {
+        assert_equals(ev.message, "Script error.");
+        assert_equals(ev.error, null, 'error');
+        assert_equals(ev.filename, "", 'filename');
+        assert_equals(ev.lineno, 0, 'lineno');
+        assert_equals(ev.colno, 0, 'colno');
+      } else {
+        assert_not_equals(ev.message, "Script error.");
+        assert_not_equals(ev.error, null);
+      }
+    }
+
+    var test1 = async_test("Errors for same-origin script shouldn't be muted");
+    var check1 = test1.step_func_done(() => check(false));
+
+    var test2 = async_test("Errors for cross-origin script should be muted");
+    var check2 = test2.step_func_done(() => check(true));
+
+    var test3 = async_test("Errors for cross-origin script should be muted " +
+                           "even if the script is once loaded as same-origin");
+    function step3() {
+      var script = document.createElement('script');
+      script.setAttribute('src', "//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py?iframe");
+      script.onerror = test3.unreached_func();
+      script.onload = test3.step_func_done(() => check(true));
+      document.body.appendChild(script);
+    }
+    function unreachable() { log.push("unexpected"); }
+</script>
+<script src="cacheable-script-throw.py" onerror="test1.unreached_func()()" onload="check1()"></script>
+<script src="//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/cacheable-script-throw.py"
+    onerror="test2.unreached_func()()" onload="check2()"></script>
+<iframe src="//{{domains[www2]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/muted-errors-iframe.html"
+    onerror="test3.unreached_func()()" onload="step3()"></iframe>
diff --git a/interfaces/accelerometer.idl b/interfaces/accelerometer.idl
index df78885..7e5509d 100644
--- a/interfaces/accelerometer.idl
+++ b/interfaces/accelerometer.idl
@@ -1,11 +1,17 @@
-[Constructor(optional SensorOptions options), SecureContext, Exposed=Window]
+enum LocalCoordinateSystem { "device", "screen" };
+
+dictionary SpatialSensorOptions : SensorOptions {
+  LocalCoordinateSystem referenceFrame = "device";
+};
+
+[Constructor(optional SpatialSensorOptions options), SecureContext, Exposed=Window]
 interface Accelerometer : Sensor {
   readonly attribute double? x;
   readonly attribute double? y;
   readonly attribute double? z;
 };
 
-[Constructor(optional SensorOptions options), SecureContext, Exposed=Window]
+[Constructor(optional SpatialSensorOptions options), SecureContext, Exposed=Window]
 interface LinearAccelerationSensor : Accelerometer {
 };
 
diff --git a/lint.whitelist b/lint.whitelist
index 2f9b44d..d3dc428 100644
--- a/lint.whitelist
+++ b/lint.whitelist
@@ -225,6 +225,7 @@
 SET TIMEOUT: user-timing/*
 SET TIMEOUT: webaudio/js/lodash.js
 SET TIMEOUT: webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html
+SET TIMEOUT: webauthn/*timeout.https.html
 SET TIMEOUT: webdriver/*
 SET TIMEOUT: webmessaging/*
 SET TIMEOUT: websockets/*
@@ -793,6 +794,8 @@
 CSS-COLLIDING-REF-NAME: css/CSS2/ui/overflow-applies-to-001-ref.xht
 CSS-COLLIDING-REF-NAME: css/CSS2/visuren/inline-formatting-context-001-ref.xht
 CSS-COLLIDING-REF-NAME: css/CSS2/linebox/inline-formatting-context-001-ref.xht
+CSS-COLLIDING-REF-NAME: css/css-transforms/individual-transform/individual-transform-1-ref.html
+CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/transforms/individual-transform-1-ref.html
 CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/support/red.png
 CSS-COLLIDING-SUPPORT-NAME: css/compositing/mix-blend-mode/support/red.png
 CSS-COLLIDING-SUPPORT-NAME: css/compositing/background-blending/support/red.png
diff --git a/media/foo-no-cors.vtt b/media/foo-no-cors.vtt
new file mode 100644
index 0000000..b533895
--- /dev/null
+++ b/media/foo-no-cors.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000
+Foo
diff --git a/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html b/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html
new file mode 100644
index 0000000..e75fb44
--- /dev/null
+++ b/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>MediaStreamTrack GetCapabilities</title>
+<p class="instructions">This test checks for the presence of
+<code>echoCancellation</code> and <code>deviceId</code> fields
+in <code>MediaStreamTrack.getCapabilities()</code> method.</p>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+  promise_test(() => {
+  return navigator.mediaDevices.getUserMedia({audio: true})
+    .then(stream => {
+      var capabilities = stream.getAudioTracks()[0].getCapabilities();
+      assert_true(undefined !== capabilities.deviceId, "MediaTrackCapabilities's deviceId should exist.");
+      assert_true(undefined !== capabilities.echoCancellation, "MediaTrackCapabilities's echoCancellation should exist.");
+    });
+});
+</script>
diff --git a/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html b/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
index 57210ef..ef66d5e 100644
--- a/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
+++ b/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.html
@@ -16,17 +16,16 @@
 var offscreenCanvas = new OffscreenCanvas(100, 50);
 var ctx = offscreenCanvas.getContext('2d');
 
-ctx.fillStyle = '#0f0';
-ctx.fillRect(0, 0, 100, 50);
 var offscreenCanvas2 = new OffscreenCanvas(0, 10);
-ctx.drawImage(offscreenCanvas2, 0, 0);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
 offscreenCanvas2.width = 10;
 offscreenCanvas2.height = 0;
-ctx.drawImage(offscreenCanvas2, 0, 0);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
 offscreenCanvas2.width = 0;
 offscreenCanvas2.height = 0;
-ctx.drawImage(offscreenCanvas2, 0, 0);
-_assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
 
 t.done();
 
diff --git a/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js b/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js
index 45dfff6..3a17cfe 100644
--- a/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js
+++ b/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerocanvas.worker.js
@@ -12,17 +12,16 @@
 var offscreenCanvas = new OffscreenCanvas(100, 50);
 var ctx = offscreenCanvas.getContext('2d');
 
-ctx.fillStyle = '#0f0';
-ctx.fillRect(0, 0, 100, 50);
 var offscreenCanvas2 = new OffscreenCanvas(0, 10);
-ctx.drawImage(offscreenCanvas2, 0, 0);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
 offscreenCanvas2.width = 10;
 offscreenCanvas2.height = 0;
-ctx.drawImage(offscreenCanvas2, 0, 0);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
+
 offscreenCanvas2.width = 0;
 offscreenCanvas2.height = 0;
-ctx.drawImage(offscreenCanvas2, 0, 0);
-_assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+assert_throws("INVALID_STATE_ERR", function() { ctx.drawImage(offscreenCanvas2, 0, 0); });
 
 t.done();
 
diff --git a/offscreen-canvas/tools/tests2d.yaml b/offscreen-canvas/tools/tests2d.yaml
index fc3b158..f542d3c 100644
--- a/offscreen-canvas/tools/tests2d.yaml
+++ b/offscreen-canvas/tools/tests2d.yaml
@@ -4417,17 +4417,16 @@
   testing:
     - 2d.drawImage.zerocanvas
   code: |
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
     var offscreenCanvas2 = new OffscreenCanvas(0, 10);
-    ctx.drawImage(offscreenCanvas2, 0, 0);
+    @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
+
     offscreenCanvas2.width = 10;
     offscreenCanvas2.height = 0;
-    ctx.drawImage(offscreenCanvas2, 0, 0);
+    @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
+
     offscreenCanvas2.width = 0;
     offscreenCanvas2.height = 0;
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    @assert pixel 50,25 ==~ 0,255,0,255;
+    @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0);
 
 - name: 2d.drawImage.wrongtype
   desc: Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError
diff --git a/picture-in-picture/not-processing-user-gesture.html b/picture-in-picture/not-processing-user-gesture.html
deleted file mode 100644
index 0075894..0000000
--- a/picture-in-picture/not-processing-user-gesture.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<title>Test request Picture-in-Picture requires a user gesture</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body></body>
-<script>
-promise_test(t => {
-  return promise_rejects(t, 'NotAllowedError',
-      document.createElement('video').requestPictureInPicture());
-}, );
-</script>
diff --git a/picture-in-picture/picture-in-picture-window.html b/picture-in-picture/picture-in-picture-window.html
new file mode 100644
index 0000000..4bee95d
--- /dev/null
+++ b/picture-in-picture/picture-in-picture-window.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Test Picture-in-Picture window</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/picture-in-picture-helpers.js"></script>
+<body></body>
+<script>
+promise_test(t => {
+  return requestPictureInPictureWithTrustedClick(document.createElement('video'))
+  .then(pipWindow => {
+    assert_not_equals(pipWindow.width, 0);
+    assert_not_equals(pipWindow.height, 0);
+  });
+}, 'Picture-in-Picture window dimensions are set after entering Picture-in-Picture');
+
+promise_test(t => {
+  const video1 = document.createElement('video');
+  const video2 = document.createElement('video');
+  return requestPictureInPictureWithTrustedClick(video1)
+  .then(pipWindow1 => {
+    return requestPictureInPictureWithTrustedClick(video2)
+    .then(pipWindow2 => {
+      assert_equals(pipWindow1.width, 0);
+      assert_equals(pipWindow1.height, 0);
+      assert_not_equals(pipWindow2.width, 0);
+      assert_not_equals(pipWindow2.height, 0);
+    });
+  });
+}, 'Picture-in-Picture window dimensions are set to 0 after entering ' +
+   'Picture-in-Picture for another video');
+
+promise_test(t => {
+  return requestPictureInPictureWithTrustedClick(document.createElement('video'))
+  .then(pipWindow => {
+    return document.exitPictureInPicture()
+    .then(() => {
+      assert_equals(pipWindow.width, 0);
+      assert_equals(pipWindow.height, 0);
+    });
+  })
+}, 'Picture-in-Picture window dimensions are set to 0 after exiting Picture-in-Picture');
+
+promise_test(t => {
+  const video = document.createElement('video');
+  let thePipWindow;
+
+  video.addEventListener('leavepictureinpicture', t.step_func_done(event => {
+    assert_equals(thePipWindow.width, 0);
+    assert_equals(thePipWindow.height, 0);
+  }));
+
+  return requestPictureInPictureWithTrustedClick(video)
+  .then(pipWindow => {
+    thePipWindow = pipWindow;
+    video.disablePictureInPicture = true;
+  });
+}, 'Picture-in-Picture window dimensions are set to 0 if ' +
+   'disablePictureInPicture becomes true');
+</script>
diff --git a/picture-in-picture/request-picture-in-picture.html b/picture-in-picture/request-picture-in-picture.html
index eaabcb7..32bbfc2 100644
--- a/picture-in-picture/request-picture-in-picture.html
+++ b/picture-in-picture/request-picture-in-picture.html
@@ -9,5 +9,21 @@
 <script>
 promise_test(t => {
   return requestPictureInPictureWithTrustedClick(document.createElement('video'));
-});
+}, 'request Picture-in-Picture resolves on user click');
+
+promise_test(t => {
+  return promise_rejects(t, 'NotAllowedError',
+      document.createElement('video').requestPictureInPicture());
+}, 'request Picture-in-Picture requires a user gesture');
+
+promise_test(t => {
+  return callWithTrustedClick(() => {
+    const first = document.createElement('video').requestPictureInPicture();
+    const second = document.createElement('video').requestPictureInPicture();
+    return Promise.all([
+      first,
+      promise_rejects(t, 'NotAllowedError', second)
+    ]);
+  });
+}, 'request Picture-in-Picture consumes user gesture');
 </script>
diff --git a/picture-in-picture/shadow-dom.html b/picture-in-picture/shadow-dom.html
new file mode 100644
index 0000000..4f4c955
--- /dev/null
+++ b/picture-in-picture/shadow-dom.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>Test for pictureInPictureElement adjustment for Shadow DOM</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/picture-in-picture-helpers.js"></script>
+<script src='../shadow-dom/resources/shadow-dom.js'></script>
+<body>
+<div id='host'>
+  <template data-mode='open' id='root'>
+    <slot></slot>
+  </template>
+  <div id='host2'>
+    <template data-mode='open' id='root2'>
+      <div id='host3'>
+        <template data-mode='open' id='root3'>
+          <video id='video'></video>
+          <div id='host4'>
+            <template data-mode='open' id='root4'>
+              <div></div>
+            </template>
+          </div>
+        </template>
+      </div>
+      <div id='host5'>
+        <template data-mode='open' id='root5'>
+          <div></div>
+        </template>
+      </div>
+    </template>
+  </div>
+</div>
+</body>
+<script>
+promise_test(t => {
+  const ids = createTestTree(host);
+  document.body.appendChild(ids.host);
+
+  assert_equals(document.pictureInPictureElement, null);
+  assert_equals(ids.root.pictureInPictureElement, null);
+  assert_equals(ids.root2.pictureInPictureElement, null);
+  assert_equals(ids.root3.pictureInPictureElement, null);
+  assert_equals(ids.root4.pictureInPictureElement, null);
+  assert_equals(ids.root5.pictureInPictureElement, null);
+
+  return requestPictureInPictureWithTrustedClick(ids.video)
+  .then(() => {
+    assert_equals(document.pictureInPictureElement, ids.host2);
+    assert_equals(ids.root.pictureInPictureElement, null);
+    assert_equals(ids.root2.pictureInPictureElement, ids.host3);
+    assert_equals(ids.root3.pictureInPictureElement, ids.video);
+    assert_equals(ids.root4.pictureInPictureElement, null);
+    assert_equals(ids.root5.pictureInPictureElement, null);
+  })
+});
+</script>
\ No newline at end of file
diff --git a/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture-manual.html b/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture-manual.html
index 92fe7f2..8ac35f8 100644
--- a/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture-manual.html
+++ b/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture-manual.html
@@ -20,7 +20,7 @@
                 var test_pointerEvent = setup_pointerevent_test("no pointercapture while pointerlock", ['mouse']);
                 var div1 = document.getElementById("div1");
                 var div2 = document.getElementById("div2");
-                
+
                 on_event(div1, 'pointerdown', function(event) {
                     div2.setPointerCapture(event.pointerId);
                 });
@@ -33,8 +33,11 @@
                         test_pointerEvent.done(); 
                     }
                 });
+                on_event(document, 'contextmenu', function(event) {
+                    event.preventDefault();
+                });
                 on_event(div2, 'pointermove', function(event) {
-                    if (got_capture && !lock_requested) {
+                    if (event.button == 2 && got_capture && !lock_requested) {
                         div1.requestPointerLock();
                         lock_requested = true;
                     }
@@ -45,6 +48,9 @@
                 on_event(div2, 'lostpointercapture', function(event) {
                     lost_capture = true;
                 });
+                on_event(document,"pointerlockerror", function() {
+                    assert_unreached("Pointer lock error");
+                })
             }
         </script>
     </head>
@@ -56,6 +62,8 @@
             <ol>
                  <li>Press left button down on the green rectangle and hold it.</li>
                  <li>Move the mouse inside the green rectangle.</li>
+                 <li>Click right button while keeping left button down</li>
+                 <li>Keep moving the mouse inside the green rectangle.</li>
             </ol>
 
             Test passes if the pointer capture is released on the yellow rectangle when the green rectangle gets the pointer lock.
diff --git a/resource-timing/clear_resource_timing_functionality.html b/resource-timing/clear_resource_timing_functionality.html
new file mode 100644
index 0000000..eaf21b4
--- /dev/null
+++ b/resource-timing/clear_resource_timing_functionality.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>This test validates the functionality of clearResourceTimings method in resource timing.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+<script>
+    setup({ explicit_done: true });
+    const context = new PerformanceContext(performance);
+    function onload_test()
+    {
+        test_equals(context.getEntriesByType('resource').length, 4, 4 + ' resource timing entries should be stored in this page.');
+        context.clearResourceTimings();
+        test_equals(context.getEntriesByType('resource').length, 0, 'No resource timing entries should be stored after clearResourceTimings.');
+        done();
+    }
+</script>
+</head>
+<body onload=onload_test()>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_TAO_cross_origin_redirect.html b/resource-timing/resource_timing_TAO_cross_origin_redirect.html
new file mode 100644
index 0000000..a91eaf8
--- /dev/null
+++ b/resource-timing/resource_timing_TAO_cross_origin_redirect.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>This test validates the values in resource timing for a timing allowed cross-origin redirect.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+
+<script>
+    setup({explicit_done: true});
+    function onload_test() {
+        const context = new PerformanceContext(performance);
+        const entry = context.getEntriesByName(document.getElementById('frameContext').src, 'resource')[0];
+
+        test_greater_than(entry.redirectStart, 0, 'redirectStart should be greater than 0 in timing allowed cross-origin redirect.');
+        test_equals(entry.redirectStart, entry.startTime, 'redirectStart should be equal to startTime in timing allowed cross-origin redirect.');
+        test_greater_or_equals(entry.redirectEnd, entry.redirectStart, 'redirectEnd should be no less than redirectStart in timing allowed cross-origin redirect.');
+        test_greater_or_equals(entry.fetchStart, entry.redirectEnd, 'fetchStart should be no less than redirectEnd in timing allowed cross-origin redirect.');
+        done();
+    }
+</script>
+
+</head>
+<body>
+<iframe id="frameContext" src="" style="width: 250px; height: 250px;"></iframe>
+<script>
+    const destUrl = '/common/redirect.py?location=/resource-timing/resources/iframe_TAO_match_origin.html';
+
+    const frameContext = document.getElementById('frameContext');
+    frameContext.onload = onload_test;
+    frameContext.src = destUrl;
+</script>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_buffer_full_when_populate_entries.html b/resource-timing/resource_timing_buffer_full_when_populate_entries.html
new file mode 100644
index 0000000..3e62b19
--- /dev/null
+++ b/resource-timing/resource_timing_buffer_full_when_populate_entries.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<title>This test validates the functionality of onresourcetimingbufferfull in resource timing.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+</head>
+<body onload=onload_test()>
+<script>
+    const context = new PerformanceContext(performance);
+    const bufferSize = 5;
+    context.setResourceTimingBufferSize(bufferSize);
+    let bufferFullCount = 0;
+    function buffer_full_callback() {
+        bufferFullCount++;
+    }
+    context.registerResourceTimingBufferFullCallback(buffer_full_callback);
+    // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
+    function appendScript(src) {
+        const script = document.createElement('script');
+        script.type = 'text/javascript';
+        script.src = src;
+        document.body.appendChild(script);
+    }
+    appendScript('resources/empty.js');
+    appendScript('resources/empty_script.js');
+    appendScript('resources/resource_timing_test0.js');
+    setup({ explicit_done: true });
+    function onload_test() {
+        test_equals(context.getEntriesByType('resource').length, bufferSize, 'There should only be |bufferSize| resource entries.');
+        test_equals(bufferFullCount, 1, 'onresourcetimingbufferfull should have been invoked once buffer is full.');
+        done();
+    }
+</script>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html b/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html
new file mode 100644
index 0000000..e42c19d
--- /dev/null
+++ b/resource-timing/resource_timing_buffer_full_when_shrink_buffer_size.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>This test validates the functionality of onresourcetimingbufferfull in resource timing.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+<script>
+    const context = new PerformanceContext(performance);
+    let bufferFullCount = 0;
+    function buffer_full_call_back() {
+        bufferFullCount++;
+    }
+    context.registerResourceTimingBufferFullCallback(buffer_full_call_back);
+    setup({ explicit_done: true });
+    function onload_test() {
+        context.setResourceTimingBufferSize(3);
+        context.setResourceTimingBufferSize(0);
+        test_equals(context.getEntriesByType('resource').length, 4, 'There are 4 scripts, and setResourceTimingBufferSize does not reduce the size.');
+        test_equals(bufferFullCount, 0, 'onresourcetimingbufferfull should not be invoked during setResourceTimingBufferSize.');
+        done();
+    }
+</script>
+</head>
+<body onload=onload_test()>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_cross_origin_redirect.html b/resource-timing/resource_timing_cross_origin_redirect.html
new file mode 100644
index 0000000..9342f5b
--- /dev/null
+++ b/resource-timing/resource_timing_cross_origin_redirect.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>This test validates the values in resource timing for a cross-origin redirect.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+
+<script>
+    setup({explicit_done: true});
+    function onload_test() {
+        const context = new PerformanceContext(performance);
+        const entry = context.getEntriesByName(document.getElementById('frameContext').src, 'resource')[0];
+
+        test_equals(entry.redirectStart, 0, 'redirectStart should be 0 in cross-origin redirect.');
+        test_equals(entry.redirectEnd, 0, 'redirectEnd should be 0 in cross-origin redirect.');
+        test_equals(entry.domainLookupStart, 0, 'domainLookupStart should be 0 in cross-origin redirect.');
+        test_equals(entry.domainLookupEnd, 0, 'domainLookupEnd should be 0 in cross-origin redirect.');
+        test_equals(entry.connectStart, 0, 'connectStart should be 0 in cross-origin redirect.');
+        test_equals(entry.connectEnd, 0, 'connectEnd should be 0 in cross-origin redirect.');
+        test_equals(entry.requestStart, 0, 'requestStart should be 0 in cross-origin redirect.');
+        test_equals(entry.responseStart, 0, 'responseStart should be 0 in cross-origin redirect.');
+        test_equals(entry.secureConnectionStart, 0, 'secureConnectionStart should be 0 in cross-origin redirect.');
+        test_greater_than(entry.fetchStart, 0, 'fetchStart should be greater than 0 in cross-origin redirect.');
+        test_greater_than(entry.responseEnd, 0, 'responseEnd should be greater than 0 in cross-origin redirect.');
+        done();
+    }
+</script>
+</head>
+<body>
+<iframe id="frameContext" src="" style="width: 250px; height: 250px;"></iframe>
+<script>
+    let destUrl = '/common/redirect.py?location=';
+    // Add www to get a cross origin frame.
+    destUrl += 'http://www.' + document.location.host + '/resource-timing/resources/blank_page_green.htm';
+
+    const frameContext = document.getElementById('frameContext');
+    frameContext.onload = onload_test;
+    frameContext.src = destUrl;
+</script>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_same_origin_redirect.html b/resource-timing/resource_timing_same_origin_redirect.html
new file mode 100644
index 0000000..d9fbf94
--- /dev/null
+++ b/resource-timing/resource_timing_same_origin_redirect.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>This test validates the values of the redirectStart/End in resource timing for a same-origin resource redirect.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+
+<script>
+    setup({explicit_done: true});
+    function onload_test() {
+        const context = new PerformanceContext(performance);
+        const entry = context.getEntriesByName(document.getElementById('frameContext').src, 'resource')[0];
+
+        test_greater_than(entry.redirectStart, 0, 'redirectStart should be greater than 0 in same-origin redirect.');
+        test_equals(entry.redirectStart, entry.startTime, 'redirectStart should be equal to startTime in same-origin redirect.');
+        test_noless_than(entry.redirectEnd, entry.redirectStart, 'redirectEnd should be no less than redirectStart in same-origin redirect.');
+        test_noless_than(entry.fetchStart, entry.redirectEnd, 'fetchStart should be no less than redirectEnd in same-origin redirect.');
+        done();
+    }
+</script>
+
+</head>
+<body>
+<iframe id="frameContext" src="" style="width: 250px; height: 250px;"></iframe>
+<script>
+    let destUrl = '/common/redirect.py';
+    destUrl += '?location=/resource-timing/resources/blank_page_green.htm';
+
+    const frameContext = document.getElementById('frameContext');
+    frameContext.onload = onload_test;
+    frameContext.src = destUrl;
+</script>
+</body>
+</html>
diff --git a/resource-timing/resource_timing_store_and_clear_during_callback.html b/resource-timing/resource_timing_store_and_clear_during_callback.html
new file mode 100644
index 0000000..218fc0c
--- /dev/null
+++ b/resource-timing/resource_timing_store_and_clear_during_callback.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head onload>
+<meta charset="utf-8" />
+<title>This test validates the behavior of read and clear operation in onresourcetimingbufferfull callback of resource timing.</title>
+<link rel="author" title="Intel" href="http://www.intel.com/" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/webperftestharness.js"></script>
+<script src="resources/webperftestharnessextension.js"></script>
+</head>
+<body onload=onload_test()>
+<script>
+    const context = new PerformanceContext(performance);
+    const resource_timing_buffer_size = 1;
+    let global_buffer = [];
+    function store_and_clear() {
+        const entryList = context.getEntriesByType('resource');
+        entryList.forEach(function (entry) {
+            global_buffer.push(entry);
+        });
+        context.clearResourceTimings();
+    }
+    context.registerResourceTimingBufferFullCallback(store_and_clear);
+    context.setResourceTimingBufferSize(resource_timing_buffer_size);
+    // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
+    function appendScript(src) {
+        const script = document.createElement('script');
+        script.type = 'text/javascript';
+        script.src = src;
+        document.body.appendChild(script);
+    }
+    appendScript('resources/empty.js');
+    appendScript('resources/empty_script.js');
+    appendScript('resources/resource_timing_test0.js');
+    setup({ explicit_done: true });
+    function onload_test() {
+        test_equals(context.getEntriesByType('resource').length, 0, 'No entry should be stored in resource timing buffer since its cleared once an item arrived.');
+        // The entry for empty.js must not be in the global buffer, but all others should be.
+        test_equals(global_buffer.length, 6, '6 resource timing entries should be moved to global buffer.');
+        const index = window.location.pathname.lastIndexOf('resource-timing');
+        const pathname = window.location.pathname.substring(0, index);
+        let expected_entries = {};
+        expected_entries[pathname + 'resources/testharness.js'] = 'script';
+        expected_entries[pathname + 'resources/testharnessreport.js'] = 'script';
+        expected_entries[pathname + 'resource-timing/resources/webperftestharness.js'] = 'script';
+        expected_entries[pathname + 'resource-timing/resources/webperftestharnessextension.js'] = 'script';
+        expected_entries[pathname + 'resource-timing/resources/empty_script.js'] = 'script';
+        expected_entries[pathname + 'resource-timing/resources/resource_timing_test0.js'] = 'script';
+        test_resource_entries(global_buffer, expected_entries);
+        done();
+    }
+</script>
+</body>
+</html>
diff --git a/service-workers/cache-storage/script-tests/cache-put.js b/service-workers/cache-storage/script-tests/cache-put.js
index 467e0b3..38d2564 100644
--- a/service-workers/cache-storage/script-tests/cache-put.js
+++ b/service-workers/cache-storage/script-tests/cache-put.js
@@ -335,4 +335,12 @@
         });
   }, 'Cache.put should store Response.redirect() correctly');
 
+cache_test(async (cache) => {
+    var request = new Request(test_url);
+    var response = new Response(new Blob([test_body]));
+    await cache.put(request, response);
+    var cachedResponse = await cache.match(request);
+    assert_equals(await cachedResponse.text(), test_body);
+  }, 'Cache.put called with simple Request and blob Response');
+
 done();
diff --git a/service-workers/cache-storage/window/cache-put.https.html b/service-workers/cache-storage/window/cache-put.https.html
index ded6e84..1ee14bd 100644
--- a/service-workers/cache-storage/window/cache-put.https.html
+++ b/service-workers/cache-storage/window/cache-put.https.html
@@ -6,3 +6,16 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="../resources/test-helpers.js"></script>
 <script src="../script-tests/cache-put.js"></script>
+<script>
+cache_test(async (cache) => {
+    var formData = new FormData();
+    formData.append("name", "value");
+
+    var request = new Request(test_url);
+    var response = new Response(formData);
+    await cache.put(request, response);
+    var cachedResponse = await cache.match(request);
+    var cachedResponseText = await cachedResponse.text();
+    assert_true(cachedResponseText.indexOf("name=\"name\"\r\n\r\nvalue") !== -1);
+  }, 'Cache.put called with simple Request and form data Response');
+</script>
diff --git a/service-workers/service-worker/fetch-request-redirect.https.html b/service-workers/service-worker/fetch-request-redirect.https.html
index 18bfa61..e8e90cf 100644
--- a/service-workers/service-worker/fetch-request-redirect.https.html
+++ b/service-workers/service-worker/fetch-request-redirect.https.html
@@ -4,19 +4,22 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/common/get-host-info.sub.js"></script>
+<script src="/common/media.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <script>
-
+var test_scope = ""
 function assert_resolves(promise, description) {
-  return promise.catch(function(reason) {
-      throw new Error(description + ' - ' + reason.message);
-  });
+    return promise.then(
+        () => test(() => {}, description + " - " + test_scope),
+        (e) => test(() => { throw e; }, description + " - " + test_scope)
+    );
 }
 
 function assert_rejects(promise, description) {
-  return promise.then(
-      function() { throw new Error(description); },
-      function() {});
+    return promise.then(
+        () => test(() => { assert_unreached(); }, description + " - " + test_scope),
+        () => test(() => {}, description + " - " + test_scope)
+    );
 }
 
 function iframe_test(url, timeout_enabled) {
@@ -52,11 +55,13 @@
 }
 
 promise_test(function(t) {
+    test_scope = "default";
+
     var SCOPE = 'resources/fetch-request-redirect-iframe.html';
     var SCRIPT = 'resources/fetch-rewrite-worker.js';
     var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
     var IMAGE_URL = base_path() + 'resources/square.png';
-    var AUDIO_URL = base_path() + 'resources/silence.oga';
+    var AUDIO_URL = getAudioURI("/media/sound_5");
     var XHR_URL = base_path() + 'resources/simple.txt';
     var HTML_URL = base_path() + 'resources/dummy.html';
 
@@ -73,108 +78,106 @@
           return wait_for_state(t, worker, 'activated');
         })
       .then(function() { return with_iframe(SCOPE); })
-      .then(function(f) {
-          frame = f;
-          return Promise.all([
+      .then(async function(f) {
+            frame = f;
             // XMLHttpRequest tests.
-            assert_resolves(frame.contentWindow.xhr(XHR_URL),
-                            'Normal XHR should succeed.'),
-            assert_resolves(frame.contentWindow.xhr(REDIRECT_TO_XHR_URL),
-                            'Redirected XHR should succeed.'),
-            assert_resolves(
+            await assert_resolves(frame.contentWindow.xhr(XHR_URL),
+                            'Normal XHR should succeed.');
+            await assert_resolves(frame.contentWindow.xhr(REDIRECT_TO_XHR_URL),
+                            'Redirected XHR should succeed.');
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&redirect-mode=follow'),
-                'Redirected XHR with Request.redirect=follow should succeed.'),
-            assert_rejects(
+                'Redirected XHR with Request.redirect=follow should succeed.');
+            await assert_rejects(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&redirect-mode=error'),
-                'Redirected XHR with Request.redirect=error should fail.'),
-            assert_rejects(
+                'Redirected XHR with Request.redirect=error should fail.');
+            await assert_rejects(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&redirect-mode=manual'),
-                'Redirected XHR with Request.redirect=manual should fail.'),
+                'Redirected XHR with Request.redirect=manual should fail.');
 
             // Image loading tests.
-            assert_resolves(frame.contentWindow.load_image(IMAGE_URL),
-                            'Normal image resource should be loaded.'),
-            assert_resolves(
+            await assert_resolves(frame.contentWindow.load_image(IMAGE_URL),
+                            'Normal image resource should be loaded.');
+            await assert_resolves(
                 frame.contentWindow.load_image(REDIRECT_TO_IMAGE_URL),
-                'Redirected image resource should be loaded.'),
-            assert_resolves(
+                'Redirected image resource should be loaded.');
+            await assert_resolves(
                 frame.contentWindow.load_image(
                     './?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
                     '&redirect-mode=follow'),
                 'Loading redirected image with Request.redirect=follow should' +
-                ' succeed.'),
-            assert_rejects(
+                ' succeed.');
+            await assert_rejects(
                 frame.contentWindow.load_image(
                     './?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
                     '&redirect-mode=error'),
                 'Loading redirected image with Request.redirect=error should ' +
-                'fail.'),
-            assert_rejects(
+                'fail.');
+            await assert_rejects(
                 frame.contentWindow.load_image(
                     './?url=' + encodeURIComponent(REDIRECT_TO_IMAGE_URL) +
                     '&redirect-mode=manual'),
                 'Loading redirected image with Request.redirect=manual should' +
-                ' fail.'),
+                ' fail.');
 
             // Audio loading tests.
-            assert_resolves(frame.contentWindow.load_audio(AUDIO_URL),
-                            'Normal audio resource should be loaded.'),
-            assert_resolves(
+            await assert_resolves(frame.contentWindow.load_audio(AUDIO_URL),
+                            'Normal audio resource should be loaded.');
+            await assert_resolves(
                 frame.contentWindow.load_audio(REDIRECT_TO_AUDIO_URL),
-                'Redirected audio resource should be loaded.'),
-            assert_resolves(
+                'Redirected audio resource should be loaded.');
+            await assert_resolves(
                 frame.contentWindow.load_audio(
                     './?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
                     '&redirect-mode=follow'),
                 'Loading redirected audio with Request.redirect=follow should' +
-                ' succeed.'),
-            assert_rejects(
+                ' succeed.');
+            await assert_rejects(
                 frame.contentWindow.load_audio(
                     './?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
                     '&redirect-mode=error'),
                 'Loading redirected audio with Request.redirect=error should ' +
-                'fail.'),
-            assert_rejects(
+                'fail.');
+            await assert_rejects(
                 frame.contentWindow.load_audio(
                     './?url=' + encodeURIComponent(REDIRECT_TO_AUDIO_URL) +
                     '&redirect-mode=manual'),
                 'Loading redirected audio with Request.redirect=manual should' +
-                ' fail.'),
+                ' fail.');
 
             // Iframe tests.
-            assert_resolves(iframe_test(HTML_URL),
-                            'Normal iframe loading should succeed.'),
-            assert_resolves(
+            await assert_resolves(iframe_test(HTML_URL),
+                            'Normal iframe loading should succeed.');
+            await assert_resolves(
                 iframe_test(REDIRECT_TO_HTML_URL),
-                'Normal redirected iframe loading should succeed.'),
-            assert_rejects(
+                'Normal redirected iframe loading should succeed.');
+            await assert_rejects(
                 iframe_test(SCOPE + '?url=' +
                             encodeURIComponent(REDIRECT_TO_HTML_URL) +
                             '&redirect-mode=follow',
                             true /* timeout_enabled */),
                 'Redirected iframe loading with Request.redirect=follow should'+
-                ' fail.'),
-            assert_rejects(
+                ' fail.');
+            await assert_rejects(
                 iframe_test(SCOPE + '?url=' +
                             encodeURIComponent(REDIRECT_TO_HTML_URL) +
                             '&redirect-mode=error',
                             true /* timeout_enabled */),
                 'Redirected iframe loading with Request.redirect=error should '+
-                'fail.'),
-            assert_resolves(
+                'fail.');
+            await assert_resolves(
                 iframe_test(SCOPE + '?url=' +
                             encodeURIComponent(REDIRECT_TO_HTML_URL) +
                             '&redirect-mode=manual',
                             true /* timeout_enabled */),
                 'Redirected iframe loading with Request.redirect=manual should'+
-                ' succeed.'),
-          ]);
+                ' succeed.');
         })
       .then(function() {
           frame.remove();
@@ -184,6 +187,8 @@
 
 // test for reponse.redirected
 promise_test(function(t) {
+    test_scope = "redirected";
+
     var SCOPE = 'resources/fetch-request-redirect-iframe.html';
     var SCRIPT = 'resources/fetch-rewrite-worker.js';
     var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
@@ -207,35 +212,34 @@
           return wait_for_state(t, worker, 'activated');
         })
       .then(function() { return with_iframe(SCOPE); })
-      .then(function(f) {
+      .then(async function(f) {
           frame = f;
-          return Promise.all([
             // XMLHttpRequest tests.
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(XHR_URL) +
                     '&expected_redirected=false' +
                     '&expected_resolves=true'),
                 'Normal XHR should be resolved and response should not be ' +
-                'redirected.'),
-            assert_resolves(
+                'redirected.');
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&expected_redirected=true' +
                     '&expected_resolves=true'),
                 'Redirected XHR should be resolved and response should be ' +
-                'redirected.'),
+                'redirected.');
 
             // tests for request's mode = cors
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(XHR_URL) +
                     '&mode=cors' +
                     '&expected_redirected=false' +
                     '&expected_resolves=true'),
                 'Normal XHR should be resolved and response should not be ' +
-                'redirected even with CORS mode.'),
-            assert_resolves(
+                'redirected even with CORS mode.');
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&mode=cors' +
@@ -243,12 +247,12 @@
                     '&expected_redirected=true' +
                     '&expected_resolves=true'),
                 'Redirected XHR should be resolved and response.redirected ' +
-                'should be redirected with CORS mode.'),
+                'should be redirected with CORS mode.');
 
             // tests for request's mode = no-cors
             // The response.redirect should be false since we will not add
             // redirected url list when redirect-mode is not follow.
-            assert_rejects(
+            await assert_rejects(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&mode=no-cors' +
@@ -256,19 +260,18 @@
                     '&expected_redirected=false' +
                     '&expected_resolves=false'),
                 'Redirected XHR should be reject and response should be ' +
-                'redirected with NO-CORS mode and redirect-mode=manual.'),
+                'redirected with NO-CORS mode and redirect-mode=manual.');
 
             // tests for redirecting to a cors
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.load_image(
                     './?url=' + encodeURIComponent(REDIRECT_TO_CROSS_ORIGIN) +
                     '&mode=no-cors' +
                     '&redirect-mode=follow' +
                     '&expected_redirected=false' +
                     '&expected_resolves=true'),
-                'Redirected COS image should be reject and response should ' +
-                'not be redirected with NO-CORS mode.'),
-          ]);
+                'Redirected CORS image should be reject and response should ' +
+                'not be redirected with NO-CORS mode.');
         })
       .then(function() {
           frame.remove();
@@ -278,6 +281,8 @@
 
 // test for reponse.redirected after cached
 promise_test(function(t) {
+    test_scope = "cache";
+
     var SCOPE = 'resources/fetch-request-redirect-iframe.html';
     var SCRIPT = 'resources/fetch-rewrite-worker.js';
     var REDIRECT_URL = base_path() + 'resources/redirect.py?Redirect=';
@@ -301,29 +306,28 @@
           return wait_for_state(t, worker, 'activated');
         })
       .then(function() { return with_iframe(SCOPE); })
-      .then(function(f) {
+      .then(async function(f) {
           frame = f;
-          return Promise.all([
             // XMLHttpRequest tests.
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(XHR_URL) +
                     '&expected_redirected=false' +
                     '&expected_resolves=true' +
                     '&cache'),
                 'Normal XHR should be resolved and response should not be ' +
-                'redirected.'),
-            assert_resolves(
+                'redirected.');
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&expected_redirected=true' +
                     '&expected_resolves=true' +
                     '&cache'),
                 'Redirected XHR should be resolved and response should be ' +
-                'redirected.'),
+                'redirected.');
 
             // tests for request's mode = cors
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(XHR_URL) +
                     '&mode=cors' +
@@ -331,8 +335,8 @@
                     '&expected_resolves=true' +
                     '&cache'),
                 'Normal XHR should be resolved and response should not be ' +
-                'redirected even with CORS mode.'),
-            assert_resolves(
+                'redirected even with CORS mode.');
+            await assert_resolves(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&mode=cors' +
@@ -341,12 +345,12 @@
                     '&expected_resolves=true' +
                     '&cache'),
                 'Redirected XHR should be resolved and response.redirected ' +
-                'should be redirected with CORS mode.'),
+                'should be redirected with CORS mode.');
 
             // tests for request's mode = no-cors
             // The response.redirect should be false since we will not add
             // redirected url list when redirect-mode is not follow.
-            assert_rejects(
+            await assert_rejects(
                 frame.contentWindow.xhr(
                     './?url=' + encodeURIComponent(REDIRECT_TO_XHR_URL) +
                     '&mode=no-cors' +
@@ -355,10 +359,10 @@
                     '&expected_resolves=false' +
                     '&cache'),
                 'Redirected XHR should be reject and response should be ' +
-                'redirected with NO-CORS mode and redirect-mode=manual.'),
+                'redirected with NO-CORS mode and redirect-mode=manual.');
 
             // tests for redirecting to a cors
-            assert_resolves(
+            await assert_resolves(
                 frame.contentWindow.load_image(
                     './?url=' + encodeURIComponent(REDIRECT_TO_CROSS_ORIGIN) +
                     '&mode=no-cors' +
@@ -366,9 +370,8 @@
                     '&expected_redirected=false' +
                     '&expected_resolves=true' +
                     '&cache'),
-                'Redirected COS image should be reject and response should ' +
-                'not be redirected with NO-CORS mode.'),
-          ]);
+                'Redirected CORS image should be reject and response should ' +
+                'not be redirected with NO-CORS mode.');
         })
       .then(function() {
           frame.remove();
diff --git a/service-workers/service-worker/http-to-https-redirect-and-register.https.html b/service-workers/service-worker/http-to-https-redirect-and-register.https.html
index d78b23a..d1c6678 100644
--- a/service-workers/service-worker/http-to-https-redirect-and-register.https.html
+++ b/service-workers/service-worker/http-to-https-redirect-and-register.https.html
@@ -8,11 +8,13 @@
 <script>
 'use strict';
 
+var host_info = get_host_info();
+
 // Loads a non-secure url in a new window, which redirects to |target_url|.
 // That page then registers a service worker, and messages back with the result.
 // Returns a promise that resolves with the result.
 function redirect_and_register(target_url) {
-  var redirect_url = get_host_info()['UNAUTHENTICATED_ORIGIN'] + base_path() +
+  var redirect_url = host_info.HTTP_REMOTE_ORIGIN + base_path() +
     'resources/redirect.py?Redirect=';
   var child = window.open(redirect_url + encodeURIComponent(target_url));
   return new Promise(resolve => {
@@ -35,7 +37,7 @@
   }, 'register on a secure page after redirect from an non-secure url');
 
 promise_test(function(t) {
-    var target_url = get_host_info()['UNAUTHENTICATED_ORIGIN'] + base_path() +
+    var target_url = host_info.HTTP_REMOTE_ORIGIN + base_path() +
       'resources/http-to-https-redirect-and-register-iframe.html';
 
     return redirect_and_register(target_url)
diff --git a/service-workers/service-worker/resources/vtt-frame.html b/service-workers/service-worker/resources/vtt-frame.html
new file mode 100644
index 0000000..c3ac803
--- /dev/null
+++ b/service-workers/service-worker/resources/vtt-frame.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page Title</title>
+<video>
+  <track>
+</video>
diff --git a/service-workers/service-worker/update-bytecheck.https.html b/service-workers/service-worker/update-bytecheck.https.html
index 6e4c6ec..ec3d15a 100644
--- a/service-workers/service-worker/update-bytecheck.https.html
+++ b/service-workers/service-worker/update-bytecheck.https.html
@@ -5,6 +5,7 @@
 <script src="resources/testharness-helpers.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
 <script>
 
 /*
@@ -26,11 +27,12 @@
                   {cors: true,  main: 'time',    imported: 'default'},
                   {cors: true,  main: 'time',    imported: 'time'   }];
 
+const host_info = get_host_info();
 settings.reduce((p, s) => {
   return p.then(promise_test(function(t) {
     var path = !s.cors ? ''
-                       : 'https://www1.web-platform.test:8443/' +
-                         'service-workers/service-worker/resources/';
+                       : host_info.HTTPS_REMOTE_ORIGIN +
+                         '/service-workers/service-worker/resources/';
     var script = 'resources/bytecheck-worker.py' +
                  '?main=' + s.main +
                  '&imported=' + s.imported +
diff --git a/service-workers/service-worker/webvtt-cross-origin.https.html b/service-workers/service-worker/webvtt-cross-origin.https.html
new file mode 100644
index 0000000..a2b741b
--- /dev/null
+++ b/service-workers/service-worker/webvtt-cross-origin.https.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>cross-origin webvtt returned by service worker is detected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/test-helpers.sub.js?pipe=sub"></script>
+<body>
+<script>
+// This file tests opaque responses for WebVTT text track. It creates
+// an iframe with a <track> element, controlled by a service worker.
+// Each test tries to load a text track, the service worker intercepts the
+// requests and responds with opaque or non-opaque responses. The opaque
+// responses should result in load errors.
+
+const host_info = get_host_info();
+const kScript = 'resources/fetch-rewrite-worker.js';
+// Add '?ignore' so the service worker falls back for the navigation.
+const kScope = 'resources/vtt-frame.html?ignore';
+let frame;
+
+function load_track(url) {
+  const track = frame.contentDocument.querySelector('track');
+  const result = new Promise((resolve, reject) => {
+      track.onload = (e => {
+          resolve('load event');
+        });
+      track.onerror = (e => {
+          resolve('error event');
+        });
+    });
+
+  track.src = url;
+  // Setting mode to hidden seems needed, or else the text track requests don't
+  // occur.
+  track.track.mode = 'hidden';
+  return result;
+}
+
+promise_test(t => {
+    return service_worker_unregister_and_register(t, kScript, kScope)
+      .then(registration => {
+          promise_test(() => {
+              frame.remove();
+              return registration.unregister();
+            }, 'restore global state');
+
+          return wait_for_state(t, registration.installing, 'activated');
+        })
+      .then(() => {
+          return with_iframe(kScope);
+        })
+      .then(f => {
+          frame = f;
+        })
+  }, 'initialize global state');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a same-origin URL.
+    url += '?url=' + host_info.HTTPS_ORIGIN + '/media/foo.vtt';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'load event');
+        });
+  }, 'same-origin text track should load');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a cross-origin URL.
+    url += '?url=' + get_host_info().HTTPS_REMOTE_ORIGIN + '/media/foo.vtt';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'error event');
+        });
+  }, 'cross-origin text track with no-cors request should not load');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a cross-origin URL that
+    // doesn't support CORS.
+    url += '?url=' + get_host_info().HTTPS_REMOTE_ORIGIN +
+        '/media/foo-no-cors.vtt';
+    // Add '&mode' to tell the service worker to do a CORS request.
+    url += '&mode=cors';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'error event');
+        });
+  }, 'cross-origin text track with rejected cors request should not load');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a cross-origin URL.
+    url += '?url=' + get_host_info().HTTPS_REMOTE_ORIGIN + '/media/foo.vtt';
+    // Add '&mode' to tell the service worker to do a CORS request.
+    url += '&mode=cors';
+    // Add '&credentials=anonymous' to allow Access-Control-Allow-Origin=* so
+    // that CORS will succeed if the service approves it.
+    url += '&credentials=anonymous';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'load event');
+        });
+  }, 'cross-origin text track with approved cors request should load');
+
+// Redirect tests.
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a same-origin URL that redirects...
+    redirector_url = host_info.HTTPS_ORIGIN + base_path() + 'resources/redirect.py?Redirect=';
+    // ... to a same-origin URL.
+    redirect_target = host_info.HTTPS_ORIGIN + '/media/foo.vtt';
+    url += '?url=' + encodeURIComponent(redirector_url + encodeURIComponent(redirect_target));
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'load event');
+        });
+  }, 'same-origin text track that redirects same-origin should load');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a same-origin URL that redirects...
+    redirector_url = host_info.HTTPS_ORIGIN + base_path() + 'resources/redirect.py?Redirect=';
+    // ... to a cross-origin URL.
+    redirect_target = host_info.HTTPS_REMOTE_ORIGIN + '/media/foo.vtt';
+    url += '?url=' + encodeURIComponent(redirector_url + encodeURIComponent(redirect_target));
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'error event');
+        });
+  }, 'same-origin text track that redirects cross-origin should not load');
+
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a same-origin URL that redirects...
+    redirector_url = host_info.HTTPS_ORIGIN + base_path() + 'resources/redirect.py?Redirect=';
+    // ... to a cross-origin URL.
+    redirect_target = host_info.HTTPS_REMOTE_ORIGIN + '/media/foo-no-cors.vtt';
+    url += '?url=' + encodeURIComponent(redirector_url + encodeURIComponent(redirect_target));
+    // Add '&mode' to tell the service worker to do a CORS request.
+    url += '&mode=cors';
+    // Add '&credentials=anonymous' to allow Access-Control-Allow-Origin=* so
+    // that CORS will succeed if the server approves it.
+    url += '&credentials=anonymous';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'error event');
+        });
+  }, 'same-origin text track that redirects to a cross-origin text track with rejected cors should not load');
+
+promise_test(t => {
+    let url = '/media/foo.vtt';
+    // Add '?url' and tell the service worker to fetch a same-origin URL that redirects...
+    redirector_url = host_info.HTTPS_ORIGIN + base_path() + 'resources/redirect.py?Redirect=';
+    // ... to a cross-origin URL.
+    redirect_target = host_info.HTTPS_REMOTE_ORIGIN + '/media/foo.vtt';
+    url += '?url=' + encodeURIComponent(redirector_url + encodeURIComponent(redirect_target));
+    // Add '&mode' to tell the service worker to do a CORS request.
+    url += '&mode=cors';
+    // Add '&credentials=anonymous' to allow Access-Control-Allow-Origin=* so
+    // that CORS will succeed if the server approves it.
+    url += '&credentials=anonymous';
+    return load_track(url)
+      .then(result => {
+          assert_equals(result, 'load event');
+        });
+  }, 'same-origin text track that redirects to a cross-origin text track with approved cors should load');
+</script>
+</body>
diff --git a/streams/readable-byte-streams/properties.js b/streams/readable-byte-streams/properties.js
index 02d0294..975fba7 100644
--- a/streams/readable-byte-streams/properties.js
+++ b/streams/readable-byte-streams/properties.js
@@ -88,7 +88,7 @@
         assert_not_equals(viewPropDesc.get, undefined, 'view should have a getter');
         assert_equals(viewPropDesc.set, undefined, 'view should not have a setter');
         assert_not_equals(byobRequest.view, undefined, 'has a non-undefined view property');
-        assert_equals(byobRequest.constructor.length, 2, 'constructor has 1 parameter');
+        assert_equals(byobRequest.constructor.length, 0, 'constructor has 0 parameters');
         assert_equals(byobRequest.respond.length, 1, 'respond has 1 parameter');
         assert_equals(byobRequest.respondWithNewView.length, 1, 'releaseLock has 1 parameter');
 
diff --git a/tools/ci/commands.json b/tools/ci/commands.json
index d682d2a..a8db428 100644
--- a/tools/ci/commands.json
+++ b/tools/ci/commands.json
@@ -2,5 +2,6 @@
     "test-jobs": {"path": "jobs.py", "script": "run", "parser": "create_parser", "help": "List test jobs that should run for a set of commits",
                   "virtualenv": false},
     "check-stability": {"path": "check_stability.py", "script": "run", "parser": "get_parser", "parse_known": true, "help": "Check test stability",
-                        "virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]}
+                        "virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]},
+  "make-hosts-file": {"path": "make_hosts_file.py", "script": "run", "parser": "create_parser", "help": "Output a hosts file to stdout", "virtualenv": false}
 }
diff --git a/tools/ci/lib.sh b/tools/ci/lib.sh
index db49ec6..2273793 100644
--- a/tools/ci/lib.sh
+++ b/tools/ci/lib.sh
@@ -6,15 +6,7 @@
     echo "## /etc/hosts ##"
     cat /etc/hosts
     sudo sed -i 's/^::1\s*localhost/::1/' /etc/hosts
-    sudo sh -c 'echo "
-127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-0.0.0.0 nonexistent-origin.web-platform.test
-" >> /etc/hosts'
+    ./wpt make-hosts-file | sudo tee -a /etc/hosts
     echo "== /etc/hosts =="
     cat /etc/hosts
     echo "----------------"
diff --git a/tools/ci/make_hosts_file.py b/tools/ci/make_hosts_file.py
new file mode 100644
index 0000000..b3c7758
--- /dev/null
+++ b/tools/ci/make_hosts_file.py
@@ -0,0 +1,19 @@
+import argparse
+import os
+
+from ..localpaths import repo_root
+
+from ..serve.serve import load_config, normalise_config, make_hosts_file
+
+def create_parser():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("address", default="127.0.0.1", nargs="?", help="Address that hosts should point at")
+    return parser
+
+def run(**kwargs):
+    config = load_config(os.path.join(repo_root, "config.default.json"),
+                         os.path.join(repo_root, "config.json"))
+
+    config = normalise_config(config, {})
+
+    print(make_hosts_file(config, kwargs["address"]))
diff --git a/tools/conftest.py b/tools/conftest.py
index 894fe62..4ce5e74 100644
--- a/tools/conftest.py
+++ b/tools/conftest.py
@@ -1,5 +1,6 @@
 import platform
 import os
+import sys
 
 from hypothesis import settings, HealthCheck
 
@@ -11,3 +12,11 @@
 
 settings.load_profile(os.getenv("HYPOTHESIS_PROFILE",
                                 "default" if impl != "PyPy" else "pypy"))
+
+# serve can't even be imported on Py3, so totally ignore it even from collection
+collect_ignore = []
+if sys.version_info[0] >= 3:
+    serve = os.path.join(os.path.dirname(__file__), "serve")
+    collect_ignore.extend([os.path.join(root, f)
+                           for root, _, files in os.walk(serve)
+                           for f in files])
diff --git a/tools/serve/serve.py b/tools/serve/serve.py
index c77badc..9c5e7ca 100644
--- a/tools/serve/serve.py
+++ b/tools/serve/serve.py
@@ -203,6 +203,8 @@
               u"天気の良い日",
               u"élève"]
 
+not_subdomains = [u"nonexistent-origin"]
+
 class RoutesBuilder(object):
     def __init__(self):
         self.forbidden_override = [("GET", "/tools/runner/*", handlers.file_handler),
@@ -468,6 +470,24 @@
             for subdomain in subdomains}
 
 
+def get_not_subdomains(host):
+    #This assumes that the tld is ascii-only or already in punycode
+    return {subdomain: (subdomain.encode("idna"), host)
+            for subdomain in not_subdomains}
+
+
+def make_hosts_file(config, host):
+    rv = []
+
+    for domain in config["domains"].values():
+        rv.append("%s\t%s\n" % (host, domain))
+
+    for not_domain in config.get("not_domains", {}).values():
+        rv.append("0.0.0.0\t%s\n" % not_domain)
+
+    return "".join(rv)
+
+
 def start_servers(host, ports, paths, routes, bind_hostname, config, ssl_config,
                   **kwargs):
     servers = defaultdict(list)
@@ -626,6 +646,7 @@
 def normalise_config(config, ports):
     host = config["external_host"] if config["external_host"] else config["host"]
     domains = get_subdomains(host)
+    not_domains = get_not_subdomains(host)
     ports_ = {}
     for scheme, ports_used in ports.iteritems():
         ports_[scheme] = ports_used
@@ -633,6 +654,9 @@
     for key, value in domains.iteritems():
         domains[key] = ".".join(value)
 
+    for key, value in not_domains.iteritems():
+        not_domains[key] = ".".join(value)
+
     domains[""] = host
 
     ports_ = {}
@@ -644,6 +668,7 @@
     config_ = config.copy()
     config_["host"] = host
     config_["domains"] = domains
+    config_["not_domains"] = not_domains
     config_["ports"] = ports_
     return config_
 
diff --git a/tools/serve/test_serve.py b/tools/serve/test_serve.py
new file mode 100644
index 0000000..3337601
--- /dev/null
+++ b/tools/serve/test_serve.py
@@ -0,0 +1,12 @@
+from . import serve
+
+def test_make_hosts_file():
+    hosts = serve.make_hosts_file({
+        "domains": {"www": "www.foo.bar.test", "www1": "www1.foo.bar.test"},
+        "not_domains": {"aaa": "aaa.foo.bar.test", "bbb": "bbb.foo.bar.test"}
+    }, "127.1.1.1")
+    lines = hosts.split("\n")
+    assert "127.1.1.1\twww.foo.bar.test" in lines
+    assert "127.1.1.1\twww1.foo.bar.test" in lines
+    assert "0.0.0.0\taaa.foo.bar.test" in lines
+    assert "0.0.0.0\tbbb.foo.bar.test" in lines
diff --git a/tools/webdriver/webdriver/transport.py b/tools/webdriver/webdriver/transport.py
index d62271f..9a1670a 100644
--- a/tools/webdriver/webdriver/transport.py
+++ b/tools/webdriver/webdriver/transport.py
@@ -39,8 +39,8 @@
         try:
             body = json.load(http_response, cls=decoder, **kwargs)
         except ValueError:
-            raise ValueError("Failed to decode response body as JSON:\n"
-                "%s" % json.dumps(body, indent=2))
+            raise ValueError("Failed to decode response body as JSON:\n" +
+                http_response.read())
 
         return cls(http_response.status, body)
 
diff --git a/tools/wpt/run.py b/tools/wpt/run.py
index f3fbcbd..236299e 100644
--- a/tools/wpt/run.py
+++ b/tools/wpt/run.py
@@ -11,6 +11,8 @@
 sys.path.insert(0, os.path.abspath(os.path.join(wpt_root, "tools")))
 
 from . import browser, utils, virtualenv
+from ..serve import serve
+
 logger = None
 
 
@@ -94,13 +96,10 @@
 
 def check_environ(product):
     if product not in ("firefox", "servo"):
-        expected_hosts = ["web-platform.test",
-                          "www.web-platform.test",
-                          "www1.web-platform.test",
-                          "www2.web-platform.test",
-                          "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
-                          "xn--lve-6lad.web-platform.test",
-                          "nonexistent-origin.web-platform.test"]
+        expected_hosts = {".".join(x)
+                          for x in serve.get_subdomains("web-platform.test").values()}
+        expected_hosts |= {".".join(x)
+                           for x in serve.get_not_subdomains("web-platform.test").values()}
         missing_hosts = set(expected_hosts)
         if platform.uname()[0] != "Windows":
             hosts_path = "/etc/hosts"
@@ -114,13 +113,17 @@
                 for host in hosts:
                     missing_hosts.discard(host)
             if missing_hosts:
-                raise WptrunError("""Missing hosts file configuration. Expected entries like:
+                if platform.uname()[0] != "Windows":
+                    message = """Missing hosts file configuration. Run
 
-%s
+python wpt make-hosts-file >> %s
 
-See README.md for more details.""" % "\n".join("%s\t%s" %
-                                               ("127.0.0.1" if "nonexistent" not in host else "0.0.0.0", host)
-                                               for host in expected_hosts))
+from a shell with Administrator privileges.""" % hosts_path
+                else:
+                    message = """Missing hosts file configuration. Run
+
+./wpt make-hosts-file | sudo tee -a %s""" % hosts_path
+                raise WptrunError(message)
 
 
 class BrowserSetup(object):
diff --git a/tools/wptrunner/MANIFEST.in b/tools/wptrunner/MANIFEST.in
index 7083923..d3b530a 100644
--- a/tools/wptrunner/MANIFEST.in
+++ b/tools/wptrunner/MANIFEST.in
@@ -5,4 +5,3 @@
 include wptrunner/*.js
 include wptrunner/executors/*.js
 include wptrunner/config.json
-include wptrunner/browsers/server-locations.txt
\ No newline at end of file
diff --git a/tools/wptrunner/setup.py b/tools/wptrunner/setup.py
index 7da5141..148a1d2 100644
--- a/tools/wptrunner/setup.py
+++ b/tools/wptrunner/setup.py
@@ -52,7 +52,6 @@
                                   "testharness_runner.html",
                                   "config.json",
                                   "wptrunner.default.ini",
-                                  "browsers/server-locations.txt",
                                   "browsers/sauce_setup/*",
                                   "prefs/*"]},
       include_package_data=True,
diff --git a/tools/wptrunner/wptrunner/browsers/chrome.py b/tools/wptrunner/wptrunner/browsers/chrome.py
index 215fccf..7f9a21c 100644
--- a/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -60,8 +60,7 @@
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class ChromeBrowser(Browser):
diff --git a/tools/wptrunner/wptrunner/browsers/chrome_android.py b/tools/wptrunner/wptrunner/browsers/chrome_android.py
index 47090be..3e832cb 100644
--- a/tools/wptrunner/wptrunner/browsers/chrome_android.py
+++ b/tools/wptrunner/wptrunner/browsers/chrome_android.py
@@ -68,8 +68,7 @@
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class ChromeAndroidBrowser(Browser):
diff --git a/tools/wptrunner/wptrunner/browsers/edge.py b/tools/wptrunner/wptrunner/browsers/edge.py
index db4ae00..094405c 100644
--- a/tools/wptrunner/wptrunner/browsers/edge.py
+++ b/tools/wptrunner/wptrunner/browsers/edge.py
@@ -38,8 +38,7 @@
     return []
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 class EdgeBrowser(Browser):
diff --git a/tools/wptrunner/wptrunner/browsers/firefox.py b/tools/wptrunner/wptrunner/browsers/firefox.py
index 6d1f58d..57e0527 100644
--- a/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -3,12 +3,12 @@
 import signal
 import subprocess
 import sys
+import tempfile
 
 import mozinfo
 import mozleak
 from mozprocess import ProcessHandler
 from mozprofile import FirefoxProfile, Preferences
-from mozprofile.permissions import ServerLocations
 from mozrunner import FirefoxRunner
 from mozrunner.utils import get_stack_fixer_function
 from mozcrash import mozcrash
@@ -23,7 +23,6 @@
 from ..executors.executormarionette import (MarionetteTestharnessExecutor,
                                             MarionetteRefTestExecutor,
                                             MarionetteWdspecExecutor)
-from ..environment import hostnames
 
 
 here = os.path.join(os.path.split(__file__)[0])
@@ -80,7 +79,8 @@
                                                          **kwargs),
             "leak_check": kwargs["leak_check"],
             "stylo_threads": kwargs["stylo_threads"],
-            "chaos_mode_flags": kwargs["chaos_mode_flags"]}
+            "chaos_mode_flags": kwargs["chaos_mode_flags"],
+            "config": kwargs["config"]}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
@@ -102,7 +102,7 @@
         if kwargs["binary_args"]:
             options["args"] = kwargs["binary_args"]
         options["prefs"] = {
-            "network.dns.localDomains": ",".join(hostnames)
+            "network.dns.localDomains": ",".join(server_config['domains'].values())
         }
         capabilities["moz:firefoxOptions"] = options
     if kwargs["certutil_binary"] is None:
@@ -143,7 +143,7 @@
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False, stackfix_dir=None,
                  binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1,
-                 chaos_mode_flags=None):
+                 chaos_mode_flags=None, config=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
@@ -159,6 +159,7 @@
         self.certutil_binary = certutil_binary
         self.e10s = e10s
         self.binary_args = binary_args
+        self.config = config
         if stackfix_dir:
             self.stack_fixer = get_stack_fixer_function(stackfix_dir,
                                                         self.symbols_path)
@@ -189,15 +190,12 @@
         if self.chaos_mode_flags is not None:
             env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
 
-        locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
-
         preferences = self.load_prefs()
 
-        self.profile = FirefoxProfile(locations=locations,
-                                      preferences=preferences)
+        self.profile = FirefoxProfile(preferences=preferences)
         self.profile.set_preferences({"marionette.port": self.marionette_port,
                                       "dom.disable_open_during_load": False,
-                                      "network.dns.localDomains": ",".join(hostnames),
+                                      "network.dns.localDomains": ",".join(self.config['domains'].values()),
                                       "network.proxy.type": 0,
                                       "places.history.enabled": False,
                                       "dom.send_after_paint_to_content": True,
diff --git a/tools/wptrunner/wptrunner/browsers/ie.py b/tools/wptrunner/wptrunner/browsers/ie.py
index 13f5827..9c0007e 100644
--- a/tools/wptrunner/wptrunner/browsers/ie.py
+++ b/tools/wptrunner/wptrunner/browsers/ie.py
@@ -42,8 +42,7 @@
     return []
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 class InternetExplorerBrowser(Browser):
diff --git a/tools/wptrunner/wptrunner/browsers/opera.py b/tools/wptrunner/wptrunner/browsers/opera.py
index 57edfa0..693a19a 100644
--- a/tools/wptrunner/wptrunner/browsers/opera.py
+++ b/tools/wptrunner/wptrunner/browsers/opera.py
@@ -60,8 +60,7 @@
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class OperaBrowser(Browser):
diff --git a/tools/wptrunner/wptrunner/browsers/sauce.py b/tools/wptrunner/wptrunner/browsers/sauce.py
index 4c1be1c..42c83c5 100644
--- a/tools/wptrunner/wptrunner/browsers/sauce.py
+++ b/tools/wptrunner/wptrunner/browsers/sauce.py
@@ -113,8 +113,7 @@
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 
@@ -128,6 +127,7 @@
 class SauceConnect():
 
     def __init__(self, **kwargs):
+        self.config = kwargs["config"]
         self.sauce_user = kwargs["sauce_user"]
         self.sauce_key = kwargs["sauce_key"]
         self.sauce_tunnel_id = kwargs["sauce_tunnel_id"]
@@ -153,8 +153,7 @@
             "--metrics-address=0.0.0.0:9876",
             "--readyfile=./sauce_is_ready",
             "--tunnel-domains",
-            "web-platform.test",
-            "*.web-platform.test"
+            ",".join(self.config['domains'].values())
         ])
 
         # Timeout config vars
diff --git a/tools/wptrunner/wptrunner/browsers/server-locations.txt b/tools/wptrunner/wptrunner/browsers/server-locations.txt
deleted file mode 100644
index 5dcaf4b..0000000
--- a/tools/wptrunner/wptrunner/browsers/server-locations.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-http://localhost:8000    primary
-
-http://web-platform.test:8000
-http://www.web-platform.test:8000
-http://www1.web-platform.test:8000
-http://www2.web-platform.test:8000
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000
-http://xn--lve-6lad.web-platform.test:8000
-
-http://web-platform.test:8001
-http://www.web-platform.test:8001
-http://www1.web-platform.test:8001
-http://www2.web-platform.test:8001
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001
-http://xn--lve-6lad.web-platform.test:8001
-
-https://web-platform.test:8443
-https://www.web-platform.test:8443
-https://www1.web-platform.test:8443
-https://www2.web-platform.test:8443
-https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8443
-https://xn--lve-6lad.web-platform.test:8443
-
-# These are actually ws servers, but until mozprofile is
-# fixed we have to pretend that they are http servers
-http://web-platform.test:8888
-http://www.web-platform.test:8888
-http://www1.web-platform.test:8888
-http://www2.web-platform.test:8888
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8888
-http://xn--lve-6lad.web-platform.test:8888
diff --git a/tools/wptrunner/wptrunner/browsers/servodriver.py b/tools/wptrunner/wptrunner/browsers/servodriver.py
index c251de8..89504b2 100644
--- a/tools/wptrunner/wptrunner/browsers/servodriver.py
+++ b/tools/wptrunner/wptrunner/browsers/servodriver.py
@@ -1,9 +1,12 @@
 import os
+import shutil
 import subprocess
 import tempfile
 
 from mozprocess import ProcessHandler
 
+from tools.serve.serve import make_hosts_file
+
 from .base import Browser, require_arg, get_free_port, browser_command, ExecutorBrowser
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executorservodriver import (ServoWebDriverTestharnessExecutor,
@@ -26,14 +29,6 @@
     "update_properties": "update_properties",
 }
 
-hosts_text = """127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-"""
-
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
@@ -69,10 +64,10 @@
     return ["debug", "os", "version", "processor", "bits"], None
 
 
-def make_hosts_file():
+def write_hosts_file(config):
     hosts_fd, hosts_path = tempfile.mkstemp()
     with os.fdopen(hosts_fd, "w") as f:
-        f.write(hosts_text)
+        f.write(make_hosts_file(config, "127.0.0.1"))
     return hosts_path
 
 
@@ -87,7 +82,7 @@
         self.webdriver_port = None
         self.proc = None
         self.debug_info = debug_info
-        self.hosts_path = make_hosts_file()
+        self.hosts_path = write_hosts_file()
         self.command = None
         self.user_stylesheets = user_stylesheets if user_stylesheets else []
 
@@ -158,6 +153,7 @@
 
     def cleanup(self):
         self.stop()
+        shutil.rmtree(os.path.dirname(self.hosts_file))
 
     def executor_browser(self):
         assert self.webdriver_port is not None
diff --git a/tools/wptrunner/wptrunner/environment.py b/tools/wptrunner/wptrunner/environment.py
index 7115341..22b02a9 100644
--- a/tools/wptrunner/wptrunner/environment.py
+++ b/tools/wptrunner/wptrunner/environment.py
@@ -18,14 +18,6 @@
 sslutils = None
 
 
-hostnames = ["web-platform.test",
-             "www.web-platform.test",
-             "www1.web-platform.test",
-             "www2.web-platform.test",
-             "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
-             "xn--lve-6lad.web-platform.test"]
-
-
 def do_delayed_imports(logger, test_paths):
     global serve, sslutils
 
diff --git a/tools/wptrunner/wptrunner/executors/executorservo.py b/tools/wptrunner/wptrunner/executors/executorservo.py
index ca8ec8a..3cf402d 100644
--- a/tools/wptrunner/wptrunner/executors/executorservo.py
+++ b/tools/wptrunner/wptrunner/executors/executorservo.py
@@ -13,6 +13,8 @@
 
 from mozprocess import ProcessHandler
 
+from tools.serve.serve import make_hosts_file
+
 from .base import (ExecutorException,
                    Protocol,
                    RefTestImplementation,
@@ -30,18 +32,10 @@
 
 extra_timeout = 5  # seconds
 
-hosts_text = """127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-"""
-
-def make_hosts_file():
+def write_hosts_file(config):
     hosts_fd, hosts_path = tempfile.mkstemp()
     with os.fdopen(hosts_fd, "w") as f:
-        f.write(hosts_text)
+        f.write(make_hosts_file(config, "127.0.0.1"))
     return hosts_path
 
 
@@ -57,7 +51,7 @@
         self.result_data = None
         self.result_flag = None
         self.protocol = Protocol(self, browser)
-        self.hosts_path = make_hosts_file()
+        self.hosts_path = write_hosts_file(server_config)
 
     def teardown(self):
         try:
diff --git a/tools/wptrunner/wptrunner/hosts.py b/tools/wptrunner/wptrunner/hosts.py
deleted file mode 100644
index 915c17f..0000000
--- a/tools/wptrunner/wptrunner/hosts.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from __future__ import unicode_literals
-
-
-class HostsLine(object):
-    def __init__(self, ip_address, canonical_hostname, aliases=None, comment=None):
-        self.ip_address = ip_address
-        self.canonical_hostname = canonical_hostname
-        self.aliases = aliases if aliases is not None else []
-        self.comment = comment
-        if self.ip_address is None:
-            assert self.canonical_hostname is None
-            assert not self.aliases
-            assert self.comment is not None
-
-    @classmethod
-    def from_string(cls, line):
-        if not line.strip():
-            return
-
-        line = line.strip()
-
-        ip_address = None
-        canonical_hostname = None
-        aliases = []
-        comment = None
-
-        comment_parts = line.split("#", 1)
-        if len(comment_parts) > 1:
-            comment = comment_parts[1]
-
-        data = comment_parts[0].strip()
-
-        if data:
-            fields = data.split()
-            if len(fields) < 2:
-                raise ValueError("Invalid hosts line")
-
-            ip_address = fields[0]
-            canonical_hostname = fields[1]
-            aliases = fields[2:]
-
-        return cls(ip_address, canonical_hostname, aliases, comment)
-
-
-class HostsFile(object):
-    def __init__(self):
-        self.data = []
-        self.by_hostname = {}
-
-    def set_host(self, host):
-        if host.canonical_hostname is None:
-            self.data.append(host)
-        elif host.canonical_hostname in self.by_hostname:
-            old_host = self.by_hostname[host.canonical_hostname]
-            old_host.ip_address = host.ip_address
-            old_host.aliases = host.aliases
-            old_host.comment = host.comment
-        else:
-            self.data.append(host)
-            self.by_hostname[host.canonical_hostname] = host
-
-    @classmethod
-    def from_file(cls, f):
-        rv = cls()
-        for line in f:
-            host = HostsLine.from_string(line)
-            if host is not None:
-                rv.set_host(host)
-        return rv
-
-    def to_string(self):
-        field_widths = [0, 0]
-        for line in self.data:
-            if line.ip_address is not None:
-                field_widths[0] = max(field_widths[0], len(line.ip_address))
-                field_widths[1] = max(field_widths[1], len(line.canonical_hostname))
-
-        lines = []
-
-        for host in self.data:
-            line = ""
-            if host.ip_address is not None:
-                ip_string = host.ip_address.ljust(field_widths[0])
-                hostname_str = host.canonical_hostname
-                if host.aliases:
-                    hostname_str = "%s %s" % (hostname_str.ljust(field_widths[1]),
-                                              " ".join(host.aliases))
-                line = "%s %s" % (ip_string, hostname_str)
-            if host.comment:
-                if line:
-                    line += " "
-                line += "#%s" % host.comment
-            lines.append(line)
-
-        lines.append("")
-
-        return "\n".join(lines)
-
-    def to_file(self, f):
-        f.write(self.to_string().encode("utf8"))
diff --git a/tools/wptrunner/wptrunner/tests/browsers/__init__.py b/tools/wptrunner/wptrunner/tests/browsers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/wptrunner/wptrunner/tests/browsers/__init__.py
diff --git a/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py b/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
index bf3e487..019b538 100644
--- a/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
+++ b/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
@@ -20,6 +20,9 @@
         exists.return_value = True
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
@@ -46,6 +49,9 @@
         exists.return_value = readyfile
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
@@ -68,6 +74,9 @@
         exists.return_value = False
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
@@ -82,3 +91,34 @@
         # Check we actually kill it after termination fails
         Popen.return_value.terminate.assert_called()
         Popen.return_value.kill.assert_called()
+
+
+def test_sauceconnect_tunnel_domains():
+    with mock.patch.object(sauce.SauceConnect, "upload_prerun_exec"),\
+            mock.patch.object(sauce.subprocess, "Popen") as Popen,\
+            mock.patch.object(sauce.os.path, "exists") as exists:
+        Popen.return_value.poll.return_value = None
+        Popen.return_value.returncode = None
+        exists.return_value = True
+
+        sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"foo": "foo.bar.example.com", "": "example.net"}
+            },
+            sauce_user="aaa",
+            sauce_key="bbb",
+            sauce_tunnel_id="ccc",
+            sauce_connect_binary="ddd")
+
+        sauce_connect.__enter__(None)
+
+        Popen.assert_called_once()
+        args, kwargs = Popen.call_args
+        cmd = args[0]
+        assert "--tunnel-domains" in cmd
+        i = cmd.index("--tunnel-domains")
+        rest = cmd[i+1:]
+        assert len(rest) >= 1
+        if len(rest) > 1:
+            assert rest[1].startswith("-"), "--tunnel-domains takes a comma separated list (not a space separated list)"
+        assert set(rest[0].split(",")) == {"foo.bar.example.com", "example.net"}
diff --git a/tools/wptrunner/wptrunner/tests/test_hosts.py b/tools/wptrunner/wptrunner/tests/test_hosts.py
deleted file mode 100644
index e7d41f3..0000000
--- a/tools/wptrunner/wptrunner/tests/test_hosts.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import unittest
-import sys
-from os.path import join, dirname
-from cStringIO import StringIO
-
-sys.path.insert(0, join(dirname(__file__), "..", ".."))
-
-from wptrunner import hosts
-
-
-class HostsTest(unittest.TestCase):
-    def do_test(self, input, expected):
-        host_file = hosts.HostsFile.from_file(StringIO(input))
-        self.assertEquals(host_file.to_string(), expected)
-
-    def test_simple(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias # comment
-# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_blank_lines(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias # comment
-
-\r
-    \t
-# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_whitespace(self):
-        self.do_test("""    \t127.0.0.1    \tlocalhost  alias # comment     \r
-    \t# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_alignment(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias
-192.168.1.1 another_host    another_alias
-""","""127.0.0.1   localhost    alias
-192.168.1.1 another_host another_alias
-""")
-
-    def test_multiple_same_name(self):
-        # The semantics are that we overwrite earlier entries with the same name
-        self.do_test("""127.0.0.1    \tlocalhost  alias
-192.168.1.1 localhost    another_alias""","""192.168.1.1 localhost another_alias
-""")
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tools/wptrunner/wptrunner/wptrunner.py b/tools/wptrunner/wptrunner/wptrunner.py
index 6677902..d3d9d07 100644
--- a/tools/wptrunner/wptrunner/wptrunner.py
+++ b/tools/wptrunner/wptrunner/wptrunner.py
@@ -218,6 +218,7 @@
                     browser_kwargs = get_browser_kwargs(test_type,
                                                         run_info,
                                                         ssl_env=ssl_env,
+                                                        config=test_environment.config,
                                                         **kwargs)
 
                     executor_cls = executor_classes.get(test_type)
diff --git a/tools/wptserve/wptserve/pipes.py b/tools/wptserve/wptserve/pipes.py
index b71c8af..534afed 100644
--- a/tools/wptserve/wptserve/pipes.py
+++ b/tools/wptserve/wptserve/pipes.py
@@ -392,6 +392,12 @@
             value = request.headers
         elif field == "GET":
             value = FirstWrapper(request.GET)
+        elif field == "domains":
+            if ('not_domains' in request.server.config and
+                    tokens[1][1] in request.server.config['not_domains']):
+                value = request.server.config['not_domains']
+            else:
+                value = request.server.config['domains']
         elif field in request.server.config:
             value = request.server.config[tokens[0][1]]
         elif field == "location":
diff --git a/webaudio/resources/biquad-filters.js b/webaudio/resources/biquad-filters.js
new file mode 100644
index 0000000..4674363
--- /dev/null
+++ b/webaudio/resources/biquad-filters.js
@@ -0,0 +1,376 @@
+// A biquad filter has a z-transform of
+// H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
+//
+// The formulas for the various filters were taken from
+// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt.
+
+
+// Lowpass filter.
+function createLowpassFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+
+  if (freq == 1) {
+    // The formula below works, except for roundoff.  When freq = 1,
+    // the filter is just a wire, so hardwire the coefficients.
+    b0 = 1;
+    b1 = 0;
+    b2 = 0;
+    a0 = 1;
+    a1 = 0;
+    a2 = 0;
+  } else {
+    let theta = Math.PI * freq;
+    let alpha = Math.sin(theta) / (2 * Math.pow(10, q / 20));
+    let cosw = Math.cos(theta);
+    let beta = (1 - cosw) / 2;
+
+    b0 = beta;
+    b1 = 2 * beta;
+    b2 = beta;
+    a0 = 1 + alpha;
+    a1 = -2 * cosw;
+    a2 = 1 - alpha;
+  }
+
+  return normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+}
+
+function createHighpassFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+
+  if (freq == 1) {
+    // The filter is 0
+    b0 = 0;
+    b1 = 0;
+    b2 = 0;
+    a0 = 1;
+    a1 = 0;
+    a2 = 0;
+  } else if (freq == 0) {
+    // The filter is 1.  Computation of coefficients below is ok, but
+    // there's a pole at 1 and a zero at 1, so round-off could make
+    // the filter unstable.
+    b0 = 1;
+    b1 = 0;
+    b2 = 0;
+    a0 = 1;
+    a1 = 0;
+    a2 = 0;
+  } else {
+    let theta = Math.PI * freq;
+    let alpha = Math.sin(theta) / (2 * Math.pow(10, q / 20));
+    let cosw = Math.cos(theta);
+    let beta = (1 + cosw) / 2;
+
+    b0 = beta;
+    b1 = -2 * beta;
+    b2 = beta;
+    a0 = 1 + alpha;
+    a1 = -2 * cosw;
+    a2 = 1 - alpha;
+  }
+
+  return normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+}
+
+function normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2) {
+  let scale = 1 / a0;
+
+  return {
+    b0: b0 * scale,
+    b1: b1 * scale,
+    b2: b2 * scale,
+    a1: a1 * scale,
+    a2: a2 * scale
+  };
+}
+
+function createBandpassFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  if (freq > 0 && freq < 1) {
+    let w0 = Math.PI * freq;
+    if (q > 0) {
+      let alpha = Math.sin(w0) / (2 * q);
+      let k = Math.cos(w0);
+
+      b0 = alpha;
+      b1 = 0;
+      b2 = -alpha;
+      a0 = 1 + alpha;
+      a1 = -2 * k;
+      a2 = 1 - alpha;
+
+      coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+    } else {
+      // q = 0, and frequency is not 0 or 1.  The above formula has a
+      // divide by zero problem.  The limit of the z-transform as q
+      // approaches 0 is 1, so set the filter that way.
+      coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+    }
+  } else {
+    // When freq = 0 or 1, the z-transform is identically 0,
+    // independent of q.
+    coef = { b0: 0, b1: 0, b2: 0, a1: 0, a2: 0 }
+  }
+
+  return coef;
+}
+
+function createLowShelfFilter(freq, q, gain) {
+  // q not used
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  let S = 1;
+  let A = Math.pow(10, gain / 40);
+
+  if (freq == 1) {
+    // The filter is just a constant gain
+    coef = {b0: A * A, b1: 0, b2: 0, a1: 0, a2: 0};
+  } else if (freq == 0) {
+    // The filter is 1
+    coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+  } else {
+    let w0 = Math.PI * freq;
+    let alpha = 1 / 2 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2);
+    let k = Math.cos(w0);
+    let k2 = 2 * Math.sqrt(A) * alpha;
+    let Ap1 = A + 1;
+    let Am1 = A - 1;
+
+    b0 = A * (Ap1 - Am1 * k + k2);
+    b1 = 2 * A * (Am1 - Ap1 * k);
+    b2 = A * (Ap1 - Am1 * k - k2);
+    a0 = Ap1 + Am1 * k + k2;
+    a1 = -2 * (Am1 + Ap1 * k);
+    a2 = Ap1 + Am1 * k - k2;
+    coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+  }
+
+  return coef;
+}
+
+function createHighShelfFilter(freq, q, gain) {
+  // q not used
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  let A = Math.pow(10, gain / 40);
+
+  if (freq == 1) {
+    // When freq = 1, the z-transform is 1
+    coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+  } else if (freq > 0) {
+    let w0 = Math.PI * freq;
+    let S = 1;
+    let alpha = 0.5 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2);
+    let k = Math.cos(w0);
+    let k2 = 2 * Math.sqrt(A) * alpha;
+    let Ap1 = A + 1;
+    let Am1 = A - 1;
+
+    b0 = A * (Ap1 + Am1 * k + k2);
+    b1 = -2 * A * (Am1 + Ap1 * k);
+    b2 = A * (Ap1 + Am1 * k - k2);
+    a0 = Ap1 - Am1 * k + k2;
+    a1 = 2 * (Am1 - Ap1 * k);
+    a2 = Ap1 - Am1 * k - k2;
+
+    coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+  } else {
+    // When freq = 0, the filter is just a gain
+    coef = {b0: A * A, b1: 0, b2: 0, a1: 0, a2: 0};
+  }
+
+  return coef;
+}
+
+function createPeakingFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  let A = Math.pow(10, gain / 40);
+
+  if (freq > 0 && freq < 1) {
+    if (q > 0) {
+      let w0 = Math.PI * freq;
+      let alpha = Math.sin(w0) / (2 * q);
+      let k = Math.cos(w0);
+
+      b0 = 1 + alpha * A;
+      b1 = -2 * k;
+      b2 = 1 - alpha * A;
+      a0 = 1 + alpha / A;
+      a1 = -2 * k;
+      a2 = 1 - alpha / A;
+
+      coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+    } else {
+      // q = 0, we have a divide by zero problem in the formulas
+      // above.  But if we look at the z-transform, we see that the
+      // limit as q approaches 0 is A^2.
+      coef = {b0: A * A, b1: 0, b2: 0, a1: 0, a2: 0};
+    }
+  } else {
+    // freq = 0 or 1, the z-transform is 1
+    coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+  }
+
+  return coef;
+}
+
+function createNotchFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  if (freq > 0 && freq < 1) {
+    if (q > 0) {
+      let w0 = Math.PI * freq;
+      let alpha = Math.sin(w0) / (2 * q);
+      let k = Math.cos(w0);
+
+      b0 = 1;
+      b1 = -2 * k;
+      b2 = 1;
+      a0 = 1 + alpha;
+      a1 = -2 * k;
+      a2 = 1 - alpha;
+      coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+    } else {
+      // When q = 0, we get a divide by zero above.  The limit of the
+      // z-transform as q approaches 0 is 0, so set the coefficients
+      // appropriately.
+      coef = {b0: 0, b1: 0, b2: 0, a1: 0, a2: 0};
+    }
+  } else {
+    // When freq = 0 or 1, the z-transform is 1
+    coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+  }
+
+  return coef;
+}
+
+function createAllpassFilter(freq, q, gain) {
+  let b0;
+  let b1;
+  let b2;
+  let a0;
+  let a1;
+  let a2;
+  let coef;
+
+  if (freq > 0 && freq < 1) {
+    if (q > 0) {
+      let w0 = Math.PI * freq;
+      let alpha = Math.sin(w0) / (2 * q);
+      let k = Math.cos(w0);
+
+      b0 = 1 - alpha;
+      b1 = -2 * k;
+      b2 = 1 + alpha;
+      a0 = 1 + alpha;
+      a1 = -2 * k;
+      a2 = 1 - alpha;
+      coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
+    } else {
+      // q = 0
+      coef = {b0: -1, b1: 0, b2: 0, a1: 0, a2: 0};
+    }
+  } else {
+    coef = {b0: 1, b1: 0, b2: 0, a1: 0, a2: 0};
+  }
+
+  return coef;
+}
+
+function filterData(filterCoef, signal, len) {
+  let y = new Array(len);
+  let b0 = filterCoef.b0;
+  let b1 = filterCoef.b1;
+  let b2 = filterCoef.b2;
+  let a1 = filterCoef.a1;
+  let a2 = filterCoef.a2;
+
+  // Prime the pump. (Assumes the signal has length >= 2!)
+  y[0] = b0 * signal[0];
+  y[1] = b0 * signal[1] + b1 * signal[0] - a1 * y[0];
+
+  // Filter all of the signal that we have.
+  for (let k = 2; k < Math.min(signal.length, len); ++k) {
+    y[k] = b0 * signal[k] + b1 * signal[k - 1] + b2 * signal[k - 2] -
+        a1 * y[k - 1] - a2 * y[k - 2];
+  }
+
+  // If we need to filter more, but don't have any signal left,
+  // assume the signal is zero.
+  for (let k = signal.length; k < len; ++k) {
+    y[k] = -a1 * y[k - 1] - a2 * y[k - 2];
+  }
+
+  return y;
+}
+
+// Map the filter type name to a function that computes the filter coefficents
+// for the given filter type.
+let filterCreatorFunction = {
+  'lowpass': createLowpassFilter,
+  'highpass': createHighpassFilter,
+  'bandpass': createBandpassFilter,
+  'lowshelf': createLowShelfFilter,
+  'highshelf': createHighShelfFilter,
+  'peaking': createPeakingFilter,
+  'notch': createNotchFilter,
+  'allpass': createAllpassFilter
+};
+
+let filterTypeName = {
+  'lowpass': 'Lowpass filter',
+  'highpass': 'Highpass filter',
+  'bandpass': 'Bandpass filter',
+  'lowshelf': 'Lowshelf filter',
+  'highshelf': 'Highshelf filter',
+  'peaking': 'Peaking filter',
+  'notch': 'Notch filter',
+  'allpass': 'Allpass filter'
+};
+
+function createFilter(filterType, freq, q, gain) {
+  return filterCreatorFunction[filterType](freq, q, gain);
+}
diff --git a/webaudio/resources/distance-model-testing.js b/webaudio/resources/distance-model-testing.js
new file mode 100644
index 0000000..1b9adde
--- /dev/null
+++ b/webaudio/resources/distance-model-testing.js
@@ -0,0 +1,193 @@
+let sampleRate = 44100.0;
+
+// How many panner nodes to create for the test.
+let nodesToCreate = 100;
+
+// Time step when each panner node starts.
+let timeStep = 0.001;
+
+// Make sure we render long enough to get all of our nodes.
+let renderLengthSeconds = timeStep * (nodesToCreate + 1);
+
+// Length of an impulse signal.
+let pulseLengthFrames = Math.round(timeStep * sampleRate);
+
+// Globals to make debugging a little easier.
+let context;
+let impulse;
+let bufferSource;
+let panner;
+let position;
+let time;
+
+// For the record, these distance formulas were taken from the OpenAL
+// spec
+// (http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.pdf),
+// not the code.  The Web Audio spec follows the OpenAL formulas.
+
+function linearDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  distance = Math.min(distance, panner.maxDistance);
+  let rolloff = panner.rolloffFactor;
+  let gain =
+      (1 -
+       rolloff * (distance - panner.refDistance) /
+           (panner.maxDistance - panner.refDistance));
+
+  return gain;
+}
+
+function inverseDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  distance = Math.min(distance, panner.maxDistance);
+  let rolloff = panner.rolloffFactor;
+  let gain = panner.refDistance /
+      (panner.refDistance + rolloff * (distance - panner.refDistance));
+
+  return gain;
+}
+
+function exponentialDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  distance = Math.min(distance, panner.maxDistance);
+  let rolloff = panner.rolloffFactor;
+  let gain = Math.pow(distance / panner.refDistance, -rolloff);
+
+  return gain;
+}
+
+// Map the distance model to the function that implements the model
+let distanceModelFunction = {
+  'linear': linearDistance,
+  'inverse': inverseDistance,
+  'exponential': exponentialDistance
+};
+
+function createGraph(context, distanceModel, nodeCount) {
+  bufferSource = new Array(nodeCount);
+  panner = new Array(nodeCount);
+  position = new Array(nodeCount);
+  time = new Array(nodesToCreate);
+
+  impulse = createImpulseBuffer(context, pulseLengthFrames);
+
+  // Create all the sources and panners.
+  //
+  // We MUST use the EQUALPOWER panning model so that we can easily
+  // figure out the gain introduced by the panner.
+  //
+  // We want to stay in the middle of the panning range, which means
+  // we want to stay on the z-axis.  If we don't, then the effect of
+  // panning model will be much more complicated.  We're not testing
+  // the panner, but the distance model, so we want the panner effect
+  // to be simple.
+  //
+  // The panners are placed at a uniform intervals between the panner
+  // reference distance and the panner max distance.  The source is
+  // also started at regular intervals.
+  for (let k = 0; k < nodeCount; ++k) {
+    bufferSource[k] = context.createBufferSource();
+    bufferSource[k].buffer = impulse;
+
+    panner[k] = context.createPanner();
+    panner[k].panningModel = 'equalpower';
+    panner[k].distanceModel = distanceModel;
+
+    let distanceStep =
+        (panner[k].maxDistance - panner[k].refDistance) / nodeCount;
+    position[k] = distanceStep * k + panner[k].refDistance;
+    panner[k].setPosition(0, 0, position[k]);
+
+    bufferSource[k].connect(panner[k]);
+    panner[k].connect(context.destination);
+
+    time[k] = k * timeStep;
+    bufferSource[k].start(time[k]);
+  }
+}
+
+// distanceModel should be the distance model string like
+// "linear", "inverse", or "exponential".
+function createTestAndRun(context, distanceModel, should) {
+  // To test the distance models, we create a number of panners at
+  // uniformly spaced intervals on the z-axis.  Each of these are
+  // started at equally spaced time intervals.  After rendering the
+  // signals, we examine where each impulse is located and the
+  // attenuation of the impulse.  The attenuation is compared
+  // against our expected attenuation.
+
+  createGraph(context, distanceModel, nodesToCreate);
+
+  return context.startRendering().then(
+      buffer => checkDistanceResult(buffer, distanceModel, should));
+}
+
+// The gain caused by the EQUALPOWER panning model, if we stay on the
+// z axis, with the default orientations.
+function equalPowerGain() {
+  return Math.SQRT1_2;
+}
+
+function checkDistanceResult(renderedBuffer, model, should) {
+  renderedData = renderedBuffer.getChannelData(0);
+
+  // The max allowed error between the actual gain and the expected
+  // value.  This is determined experimentally.  Set to 0 to see
+  // what the actual errors are.
+  let maxAllowedError = 3.3e-6;
+
+  let success = true;
+
+  // Number of impulses we found in the rendered result.
+  let impulseCount = 0;
+
+  // Maximum relative error in the gain of the impulses.
+  let maxError = 0;
+
+  // Array of locations of the impulses that were not at the
+  // expected location.  (Contains the actual and expected frame
+  // of the impulse.)
+  let impulsePositionErrors = new Array();
+
+  // Step through the rendered data to find all the non-zero points
+  // so we can find where our distance-attenuated impulses are.
+  // These are tested against the expected attenuations at that
+  // distance.
+  for (let k = 0; k < renderedData.length; ++k) {
+    if (renderedData[k] != 0) {
+      // Convert from string to index.
+      let distanceFunction = distanceModelFunction[model];
+      let expected =
+          distanceFunction(panner[impulseCount], 0, 0, position[impulseCount]);
+
+      // Adjust for the center-panning of the EQUALPOWER panning
+      // model that we're using.
+      expected *= equalPowerGain();
+
+      let error = Math.abs(renderedData[k] - expected) / Math.abs(expected);
+
+      maxError = Math.max(maxError, Math.abs(error));
+
+      should(renderedData[k]).beCloseTo(expected, {threshold: maxAllowedError});
+
+      // Keep track of any impulses that aren't where we expect them
+      // to be.
+      let expectedOffset = timeToSampleFrame(time[impulseCount], sampleRate);
+      if (k != expectedOffset) {
+        impulsePositionErrors.push({actual: k, expected: expectedOffset});
+      }
+      ++impulseCount;
+    }
+  }
+  should(impulseCount, 'Number of impulses').beEqualTo(nodesToCreate);
+
+  should(maxError, 'Max error in distance gains')
+      .beLessThanOrEqualTo(maxAllowedError);
+
+  // Display any timing errors that we found.
+  if (impulsePositionErrors.length > 0) {
+    let actual = impulsePositionErrors.map(x => x.actual);
+    let expected = impulsePositionErrors.map(x => x.expected);
+    should(actual, 'Actual impulse positions found').beEqualToArray(expected);
+  }
+}
diff --git a/webaudio/resources/panner-formulas.js b/webaudio/resources/panner-formulas.js
new file mode 100644
index 0000000..ae6f516
--- /dev/null
+++ b/webaudio/resources/panner-formulas.js
@@ -0,0 +1,190 @@
+// For the record, these distance formulas were taken from the OpenAL
+// spec
+// (http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.pdf),
+// not the code.  The Web Audio spec follows the OpenAL formulas.
+
+function linearDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  let dref = Math.min(panner.refDistance, panner.maxDistance);
+  let dmax = Math.max(panner.refDistance, panner.maxDistance);
+  distance = Math.max(Math.min(distance, dmax), dref);
+  let rolloff = Math.max(Math.min(panner.rolloffFactor, 1), 0);
+  if (dref === dmax)
+    return 1 - rolloff;
+
+  let gain = (1 - rolloff * (distance - dref) / (dmax - dref));
+
+  return gain;
+}
+
+function inverseDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  distance = Math.max(distance, panner.refDistance);
+  let rolloff = panner.rolloffFactor;
+  let gain = panner.refDistance /
+      (panner.refDistance +
+       rolloff * (Math.max(distance, panner.refDistance) - panner.refDistance));
+
+  return gain;
+}
+
+function exponentialDistance(panner, x, y, z) {
+  let distance = Math.sqrt(x * x + y * y + z * z);
+  distance = Math.max(distance, panner.refDistance);
+  let rolloff = panner.rolloffFactor;
+  let gain = Math.pow(distance / panner.refDistance, -rolloff);
+
+  return gain;
+}
+
+// Simple implementations of 3D vectors implemented as a 3-element array.
+
+// x - y
+function vec3Sub(x, y) {
+  let z = new Float32Array(3);
+  z[0] = x[0] - y[0];
+  z[1] = x[1] - y[1];
+  z[2] = x[2] - y[2];
+
+  return z;
+}
+
+// x/|x|
+function vec3Normalize(x) {
+  let mag = Math.hypot(...x);
+  return x.map(function(c) {
+    return c / mag;
+  });
+}
+
+// x == 0?
+function vec3IsZero(x) {
+  return x[0] === 0 && x[1] === 0 && x[2] === 0;
+}
+
+// Vector cross product
+function vec3Cross(u, v) {
+  let cross = new Float32Array(3);
+  cross[0] = u[1] * v[2] - u[2] * v[1];
+  cross[1] = u[2] * v[0] - u[0] * v[2];
+  cross[2] = u[0] * v[1] - u[1] * v[0];
+  return cross;
+}
+
+// Dot product
+function vec3Dot(x, y) {
+  return x[0] * y[0] + x[1] * y[1] + x[2] * y[2];
+}
+
+// a*x, for scalar a
+function vec3Scale(a, x) {
+  return x.map(function(c) {
+    return a * c;
+  });
+}
+
+function calculateAzimuth(source, listener, listenerForward, listenerUp) {
+  let sourceListener = vec3Sub(source, listener);
+
+  if (vec3IsZero(sourceListener))
+    return 0;
+
+  sourceListener = vec3Normalize(sourceListener);
+
+  let listenerRight = vec3Normalize(vec3Cross(listenerForward, listenerUp));
+  let listenerForwardNorm = vec3Normalize(listenerForward);
+
+  let up = vec3Cross(listenerRight, listenerForwardNorm);
+  let upProjection = vec3Dot(sourceListener, up);
+
+  let projectedSource =
+      vec3Normalize(vec3Sub(sourceListener, vec3Scale(upProjection, up)));
+
+  let azimuth =
+      180 / Math.PI * Math.acos(vec3Dot(projectedSource, listenerRight));
+
+  // Source in front or behind the listener
+  let frontBack = vec3Dot(projectedSource, listenerForwardNorm);
+  if (frontBack < 0)
+    azimuth = 360 - azimuth;
+
+  // Make azimuth relative to "front" and not "right" listener vector.
+  if (azimuth >= 0 && azimuth <= 270)
+    azimuth = 90 - azimuth;
+  else
+    azimuth = 450 - azimuth;
+
+  // We don't need elevation, so we're skipping that computation.
+  return azimuth;
+}
+
+// Map our position angle to the azimuth angle (in degrees).
+//
+// An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg.
+function angleToAzimuth(angle) {
+  return 90 - angle * 180 / Math.PI;
+}
+
+// The gain caused by the EQUALPOWER panning model
+function equalPowerGain(azimuth, numberOfChannels) {
+  let halfPi = Math.PI / 2;
+
+  if (azimuth < -90)
+    azimuth = -180 - azimuth;
+  else
+    azimuth = 180 - azimuth;
+
+  if (numberOfChannels == 1) {
+    let panPosition = (azimuth + 90) / 180;
+
+    let gainL = Math.cos(halfPi * panPosition);
+    let gainR = Math.sin(halfPi * panPosition);
+
+    return {left: gainL, right: gainR};
+  } else {
+    if (azimuth <= 0) {
+      let panPosition = (azimuth + 90) / 90;
+
+      let gainL = Math.cos(halfPi * panPosition);
+      let gainR = Math.sin(halfPi * panPosition);
+
+      return {left: gainL, right: gainR};
+    } else {
+      let panPosition = azimuth / 90;
+
+      let gainL = Math.cos(halfPi * panPosition);
+      let gainR = Math.sin(halfPi * panPosition);
+
+      return {left: gainL, right: gainR};
+    }
+  }
+}
+
+function applyPanner(azimuth, srcL, srcR, numberOfChannels) {
+  let length = srcL.length;
+  let outL = new Float32Array(length);
+  let outR = new Float32Array(length);
+
+  if (numberOfChannels == 1) {
+    for (let k = 0; k < length; ++k) {
+      let gains = equalPowerGain(azimuth[k], numberOfChannels);
+
+      outL[k] = srcL[k] * gains.left;
+      outR[k] = srcR[k] * gains.right;
+    }
+  } else {
+    for (let k = 0; k < length; ++k) {
+      let gains = equalPowerGain(azimuth[k], numberOfChannels);
+
+      if (azimuth[k] <= 0) {
+        outL[k] = srcL[k] + srcR[k] * gains.left;
+        outR[k] = srcR[k] * gains.right;
+      } else {
+        outL[k] = srcL[k] * gains.left;
+        outR[k] = srcR[k] + srcL[k] * gains.right;
+      }
+    }
+  }
+
+  return {left: outL, right: outR};
+}
diff --git a/webaudio/resources/panner-model-testing.js b/webaudio/resources/panner-model-testing.js
new file mode 100644
index 0000000..662fb1d
--- /dev/null
+++ b/webaudio/resources/panner-model-testing.js
@@ -0,0 +1,181 @@
+let sampleRate = 44100.0;
+
+let numberOfChannels = 1;
+
+// Time step when each panner node starts.
+let timeStep = 0.001;
+
+// Length of the impulse signal.
+let pulseLengthFrames = Math.round(timeStep * sampleRate);
+
+// How many panner nodes to create for the test
+let nodesToCreate = 100;
+
+// Be sure we render long enough for all of our nodes.
+let renderLengthSeconds = timeStep * (nodesToCreate + 1);
+
+// These are global mostly for debugging.
+let context;
+let impulse;
+let bufferSource;
+let panner;
+let position;
+let time;
+
+let renderedBuffer;
+let renderedLeft;
+let renderedRight;
+
+function createGraph(context, nodeCount, positionSetter) {
+  bufferSource = new Array(nodeCount);
+  panner = new Array(nodeCount);
+  position = new Array(nodeCount);
+  time = new Array(nodeCount);
+  // Angle between panner locations.  (nodeCount - 1 because we want
+  // to include both 0 and 180 deg.
+  let angleStep = Math.PI / (nodeCount - 1);
+
+  if (numberOfChannels == 2) {
+    impulse = createStereoImpulseBuffer(context, pulseLengthFrames);
+  } else
+    impulse = createImpulseBuffer(context, pulseLengthFrames);
+
+  for (let k = 0; k < nodeCount; ++k) {
+    bufferSource[k] = context.createBufferSource();
+    bufferSource[k].buffer = impulse;
+
+    panner[k] = context.createPanner();
+    panner[k].panningModel = 'equalpower';
+    panner[k].distanceModel = 'linear';
+
+    let angle = angleStep * k;
+    position[k] = {angle: angle, x: Math.cos(angle), z: Math.sin(angle)};
+    positionSetter(panner[k], position[k].x, 0, position[k].z);
+
+    bufferSource[k].connect(panner[k]);
+    panner[k].connect(context.destination);
+
+    // Start the source
+    time[k] = k * timeStep;
+    bufferSource[k].start(time[k]);
+  }
+}
+
+function createTestAndRun(
+    context, should, nodeCount, numberOfSourceChannels, positionSetter) {
+  numberOfChannels = numberOfSourceChannels;
+
+  createGraph(context, nodeCount, positionSetter);
+
+  return context.startRendering().then(buffer => checkResult(buffer, should));
+}
+
+// Map our position angle to the azimuth angle (in degrees).
+//
+// An angle of 0 corresponds to an azimuth of 90 deg; pi, to -90 deg.
+function angleToAzimuth(angle) {
+  return 90 - angle * 180 / Math.PI;
+}
+
+// The gain caused by the EQUALPOWER panning model
+function equalPowerGain(angle) {
+  let azimuth = angleToAzimuth(angle);
+
+  if (numberOfChannels == 1) {
+    let panPosition = (azimuth + 90) / 180;
+
+    let gainL = Math.cos(0.5 * Math.PI * panPosition);
+    let gainR = Math.sin(0.5 * Math.PI * panPosition);
+
+    return {left: gainL, right: gainR};
+  } else {
+    if (azimuth <= 0) {
+      let panPosition = (azimuth + 90) / 90;
+
+      let gainL = 1 + Math.cos(0.5 * Math.PI * panPosition);
+      let gainR = Math.sin(0.5 * Math.PI * panPosition);
+
+      return {left: gainL, right: gainR};
+    } else {
+      let panPosition = azimuth / 90;
+
+      let gainL = Math.cos(0.5 * Math.PI * panPosition);
+      let gainR = 1 + Math.sin(0.5 * Math.PI * panPosition);
+
+      return {left: gainL, right: gainR};
+    }
+  }
+}
+
+function checkResult(renderedBuffer, should) {
+  renderedLeft = renderedBuffer.getChannelData(0);
+  renderedRight = renderedBuffer.getChannelData(1);
+
+  // The max error we allow between the rendered impulse and the
+  // expected value.  This value is experimentally determined.  Set
+  // to 0 to make the test fail to see what the actual error is.
+  let maxAllowedError = 1.3e-6;
+
+  let success = true;
+
+  // Number of impulses found in the rendered result.
+  let impulseCount = 0;
+
+  // Max (relative) error and the index of the maxima for the left
+  // and right channels.
+  let maxErrorL = 0;
+  let maxErrorIndexL = 0;
+  let maxErrorR = 0;
+  let maxErrorIndexR = 0;
+
+  // Number of impulses that don't match our expected locations.
+  let timeCount = 0;
+
+  // Locations of where the impulses aren't at the expected locations.
+  let timeErrors = new Array();
+
+  for (let k = 0; k < renderedLeft.length; ++k) {
+    // We assume that the left and right channels start at the same instant.
+    if (renderedLeft[k] != 0 || renderedRight[k] != 0) {
+      // The expected gain for the left and right channels.
+      let pannerGain = equalPowerGain(position[impulseCount].angle);
+      let expectedL = pannerGain.left;
+      let expectedR = pannerGain.right;
+
+      // Absolute error in the gain.
+      let errorL = Math.abs(renderedLeft[k] - expectedL);
+      let errorR = Math.abs(renderedRight[k] - expectedR);
+
+      if (Math.abs(errorL) > maxErrorL) {
+        maxErrorL = Math.abs(errorL);
+        maxErrorIndexL = impulseCount;
+      }
+      if (Math.abs(errorR) > maxErrorR) {
+        maxErrorR = Math.abs(errorR);
+        maxErrorIndexR = impulseCount;
+      }
+
+      // Keep track of the impulses that didn't show up where we
+      // expected them to be.
+      let expectedOffset = timeToSampleFrame(time[impulseCount], sampleRate);
+      if (k != expectedOffset) {
+        timeErrors[timeCount] = {actual: k, expected: expectedOffset};
+        ++timeCount;
+      }
+      ++impulseCount;
+    }
+  }
+
+  should(impulseCount, 'Number of impulses found').beEqualTo(nodesToCreate);
+
+  should(
+      timeErrors.map(x => x.actual),
+      'Offsets of impulses at the wrong position')
+      .beEqualToArray(timeErrors.map(x => x.expected));
+
+  should(maxErrorL, 'Error in left channel gain values')
+      .beLessThanOrEqualTo(maxAllowedError);
+
+  should(maxErrorR, 'Error in right channel gain values')
+      .beLessThanOrEqualTo(maxAllowedError);
+}
diff --git a/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html
new file mode 100644
index 0000000..da36d58
--- /dev/null
+++ b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-basic.html
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Basic IIRFilterNode Properties
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 48000;
+      let testFrames = 100;
+
+      // Global context that can be used by the individual tasks. It must be
+      // defined by the initialize task.
+      let context;
+
+      let audit = Audit.createTaskRunner();
+
+      audit.define('initialize', (task, should) => {
+        should(() => {
+          context = new OfflineAudioContext(1, testFrames, sampleRate);
+        }, 'Initialize context for testing').notThrow();
+        task.done();
+      });
+
+      audit.define('existence', (task, should) => {
+        should(context.createIIRFilter, 'context.createIIRFilter').exist();
+        task.done();
+      });
+
+      audit.define('parameters', (task, should) => {
+        // Create a really simple IIR filter. Doesn't much matter what.
+        let coef = Float32Array.from([1]);
+
+        let f = context.createIIRFilter(coef, coef);
+
+        should(f.numberOfInputs, 'numberOfInputs').beEqualTo(1);
+        should(f.numberOfOutputs, 'numberOfOutputs').beEqualTo(1);
+        should(f.channelCountMode, 'channelCountMode').beEqualTo('max');
+        should(f.channelInterpretation, 'channelInterpretation')
+            .beEqualTo('speakers');
+
+        task.done();
+      });
+
+      audit.define('exceptions-createIIRFilter', (task, should) => {
+        should(function() {
+          // Two args are required.
+          context.createIIRFilter();
+        }, 'createIIRFilter()').throw('TypeError');
+
+        should(function() {
+          // Two args are required.
+          context.createIIRFilter(new Float32Array(1));
+        }, 'createIIRFilter(new Float32Array(1))').throw('TypeError');
+
+        should(function() {
+          // null is not valid
+          context.createIIRFilter(null, null);
+        }, 'createIIRFilter(null, null)').throw('TypeError');
+
+        should(function() {
+          // There has to be at least one coefficient.
+          context.createIIRFilter([], []);
+        }, 'createIIRFilter([], [])').throw('NotSupportedError');
+
+        should(function() {
+          // There has to be at least one coefficient.
+          context.createIIRFilter([1], []);
+        }, 'createIIRFilter([1], [])').throw('NotSupportedError');
+
+        should(function() {
+          // There has to be at least one coefficient.
+          context.createIIRFilter([], [1]);
+        }, 'createIIRFilter([], [1])').throw('NotSupportedError');
+
+        should(
+            function() {
+              // Max allowed size for the coefficient arrays.
+              let fb = new Float32Array(20);
+              fb[0] = 1;
+              context.createIIRFilter(fb, fb);
+            },
+            'createIIRFilter(new Float32Array(20), new Float32Array(20))')
+            .notThrow();
+
+        should(
+            function() {
+              // Max allowed size for the feedforward coefficient array.
+              let coef = new Float32Array(21);
+              coef[0] = 1;
+              context.createIIRFilter(coef, [1]);
+            },
+            'createIIRFilter(new Float32Array(21), [1])')
+            .throw('NotSupportedError');
+
+        should(
+            function() {
+              // Max allowed size for the feedback coefficient array.
+              let coef = new Float32Array(21);
+              coef[0] = 1;
+              context.createIIRFilter([1], coef);
+            },
+            'createIIRFilter([1], new Float32Array(21))')
+            .throw('NotSupportedError');
+
+        should(
+            function() {
+              // First feedback coefficient can't be 0.
+              context.createIIRFilter([1], new Float32Array(2));
+            },
+            'createIIRFilter([1], new Float32Array(2))')
+            .throw('InvalidStateError');
+
+        should(
+            function() {
+              // feedforward coefficients can't all be zero.
+              context.createIIRFilter(new Float32Array(10), [1]);
+            },
+            'createIIRFilter(new Float32Array(10), [1])')
+            .throw('InvalidStateError');
+
+        should(function() {
+          // Feedback coefficients must be finite.
+          context.createIIRFilter([1], [1, Infinity, NaN]);
+        }, 'createIIRFilter([1], [1, NaN, Infinity])').throw('TypeError');
+
+        should(function() {
+          // Feedforward coefficients must be finite.
+          context.createIIRFilter([1, Infinity, NaN], [1]);
+        }, 'createIIRFilter([1, NaN, Infinity], [1])').throw('TypeError');
+
+        should(function() {
+          // Test that random junk in the array is converted to NaN.
+          context.createIIRFilter([1, 'abc', []], [1]);
+        }, 'createIIRFilter([1, \'abc\', []], [1])').throw('TypeError');
+
+        task.done();
+      });
+
+      audit.define('exceptions-getFrequencyData', (task, should) => {
+        // Create a really simple IIR filter. Doesn't much matter what.
+        let coef = Float32Array.from([1]);
+
+        let f = context.createIIRFilter(coef, coef);
+
+        should(
+            function() {
+              // frequencyHz can't be null.
+              f.getFrequencyResponse(
+                  null, new Float32Array(1), new Float32Array(1));
+            },
+            'getFrequencyResponse(null, new Float32Array(1), new Float32Array(1))')
+            .throw('TypeError');
+
+        should(
+            function() {
+              // magResponse can't be null.
+              f.getFrequencyResponse(
+                  new Float32Array(1), null, new Float32Array(1));
+            },
+            'getFrequencyResponse(new Float32Array(1), null, new Float32Array(1))')
+            .throw('TypeError');
+
+        should(
+            function() {
+              // phaseResponse can't be null.
+              f.getFrequencyResponse(
+                  new Float32Array(1), new Float32Array(1), null);
+            },
+            'getFrequencyResponse(new Float32Array(1), new Float32Array(1), null)')
+            .throw('TypeError');
+
+        should(
+            function() {
+              // magResponse array must the same length as frequencyHz
+              f.getFrequencyResponse(
+                  new Float32Array(10), new Float32Array(1),
+                  new Float32Array(20));
+            },
+            'getFrequencyResponse(new Float32Array(10), new Float32Array(1), new Float32Array(20))')
+            .throw('InvalidAccessError');
+
+        should(
+            function() {
+              // phaseResponse array must be the same length as frequencyHz
+              f.getFrequencyResponse(
+                  new Float32Array(10), new Float32Array(20),
+                  new Float32Array(1));
+            },
+            'getFrequencyResponse(new Float32Array(10), new Float32Array(20), new Float32Array(1))')
+            .throw('InvalidAccessError');
+
+        task.done();
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-getFrequencyResponse.html b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-getFrequencyResponse.html
new file mode 100644
index 0000000..c98555f
--- /dev/null
+++ b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter-getFrequencyResponse.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test IIRFilter getFrequencyResponse() functionality
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/biquad-filters.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 48000;
+      // Some short duration; we're not actually looking at the rendered output.
+      let testDurationSec = 0.01;
+
+      // Number of frequency samples to take.
+      let numberOfFrequencies = 1000;
+
+      let audit = Audit.createTaskRunner();
+
+
+      // Compute a set of linearly spaced frequencies.
+      function createFrequencies(nFrequencies, sampleRate) {
+        let frequencies = new Float32Array(nFrequencies);
+        let nyquist = sampleRate / 2;
+        let freqDelta = nyquist / nFrequencies;
+
+        for (let k = 0; k < nFrequencies; ++k) {
+          frequencies[k] = k * freqDelta;
+        }
+
+        return frequencies;
+      }
+
+      audit.define('1-pole IIR', (task, should) => {
+        let context = new OfflineAudioContext(
+            1, testDurationSec * sampleRate, sampleRate);
+
+        let iir = context.createIIRFilter([1], [1, -0.9]);
+        let frequencies =
+            createFrequencies(numberOfFrequencies, context.sampleRate);
+
+        let iirMag = new Float32Array(numberOfFrequencies);
+        let iirPhase = new Float32Array(numberOfFrequencies);
+        let trueMag = new Float32Array(numberOfFrequencies);
+        let truePhase = new Float32Array(numberOfFrequencies);
+
+        // The IIR filter is
+        //   H(z) = 1/(1 - 0.9*z^(-1)).
+        //
+        // The frequency response is
+        //   H(exp(j*w)) = 1/(1 - 0.9*exp(-j*w)).
+        //
+        // Thus, the magnitude is
+        //   |H(exp(j*w))| = 1/sqrt(1.81-1.8*cos(w)).
+        //
+        // The phase is
+        //   arg(H(exp(j*w)) = atan(0.9*sin(w)/(.9*cos(w)-1))
+
+        let frequencyScale = Math.PI / (sampleRate / 2);
+
+        for (let k = 0; k < frequencies.length; ++k) {
+          let omega = frequencyScale * frequencies[k];
+          trueMag[k] = 1 / Math.sqrt(1.81 - 1.8 * Math.cos(omega));
+          truePhase[k] =
+              Math.atan(0.9 * Math.sin(omega) / (0.9 * Math.cos(omega) - 1));
+        }
+
+        iir.getFrequencyResponse(frequencies, iirMag, iirPhase);
+
+        // Thresholds were experimentally determined.
+        should(iirMag, '1-pole IIR Magnitude Response')
+            .beCloseToArray(trueMag, {absoluteThreshold: 2.8611e-6});
+        should(iirPhase, '1-pole IIR Phase Response')
+            .beCloseToArray(truePhase, {absoluteThreshold: 1.7882e-7});
+
+        task.done();
+      });
+
+      audit.define('compare IIR and biquad', (task, should) => {
+        // Create an IIR filter equivalent to the biquad filter. Compute the
+        // frequency response for both and verify that they are the same.
+        let context = new OfflineAudioContext(
+            1, testDurationSec * sampleRate, sampleRate);
+
+        let biquad = context.createBiquadFilter();
+        let coef = createFilter(
+            biquad.type, biquad.frequency.value / (context.sampleRate / 2),
+            biquad.Q.value, biquad.gain.value);
+
+        let iir = context.createIIRFilter(
+            [coef.b0, coef.b1, coef.b2], [1, coef.a1, coef.a2]);
+
+        let frequencies =
+            createFrequencies(numberOfFrequencies, context.sampleRate);
+        let biquadMag = new Float32Array(numberOfFrequencies);
+        let biquadPhase = new Float32Array(numberOfFrequencies);
+        let iirMag = new Float32Array(numberOfFrequencies);
+        let iirPhase = new Float32Array(numberOfFrequencies);
+
+        biquad.getFrequencyResponse(frequencies, biquadMag, biquadPhase);
+        iir.getFrequencyResponse(frequencies, iirMag, iirPhase);
+
+        // Thresholds were experimentally determined.
+        should(iirMag, 'IIR Magnitude Response').beCloseToArray(biquadMag, {
+          absoluteThreshold: 2.7419e-5
+        });
+        should(iirPhase, 'IIR Phase Response').beCloseToArray(biquadPhase, {
+          absoluteThreshold: 2.7657e-5
+        });
+
+        task.done();
+      });
+
+      audit.define(
+          {
+            label: 'getFrequencyResponse',
+            description: 'Test out-of-bounds frequency values'
+          },
+          (task, should) => {
+            let context = new OfflineAudioContext(1, 1, sampleRate);
+            let filter = new IIRFilterNode(
+                context, {feedforward: [1], feedback: [1, -.9]});
+
+            // Frequencies to test.  These are all outside the valid range of
+            // frequencies of 0 to Nyquist.
+            let freq = new Float32Array(2);
+            freq[0] = -1;
+            freq[1] = context.sampleRate / 2 + 1;
+
+            let mag = new Float32Array(freq.length);
+            let phase = new Float32Array(freq.length);
+
+            filter.getFrequencyResponse(freq, mag, phase);
+
+            // Verify that the returned magnitude and phase entries are alL NaN
+            // since the frequencies are outside the valid range
+            for (let k = 0; k < mag.length; ++k) {
+              should(mag[k],
+                  'Magnitude response at frequency ' + freq[k])
+                  .beNaN();
+            }
+
+            for (let k = 0; k < phase.length; ++k) {
+              should(phase[k],
+                  'Phase response at frequency ' + freq[k])
+                  .beNaN();
+            }
+
+            task.done();
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter.html b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter.html
new file mode 100644
index 0000000..aa38a6b
--- /dev/null
+++ b/webaudio/the-audio-api/the-iirfilternode-interface/iirfilter.html
@@ -0,0 +1,572 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Basic IIRFilterNode Operation
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/biquad-filters.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 24000;
+      let testDurationSec = 0.25;
+      let testFrames = testDurationSec * sampleRate;
+
+      let audit = Audit.createTaskRunner();
+
+      audit.define('coefficient-normalization', (task, should) => {
+        // Test that the feedback coefficients are normalized.  Do this be
+        // creating two IIRFilterNodes.  One has normalized coefficients, and
+        // one doesn't.  Compute the difference and make sure they're the same.
+        let context = new OfflineAudioContext(2, testFrames, sampleRate);
+
+        // Use a simple impulse as the source.
+        let buffer = context.createBuffer(1, 1, sampleRate);
+        buffer.getChannelData(0)[0] = 1;
+        let source = context.createBufferSource();
+        source.buffer = buffer;
+
+        // Gain node for computing the difference between the filters.
+        let gain = context.createGain();
+        gain.gain.value = -1;
+
+        // The IIR filters.  Use a common feedforward array.
+        let ff = [1];
+
+        let fb1 = [1, .9];
+
+        let fb2 = new Float64Array(2);
+        // Scale the feedback coefficients by an arbitrary factor.
+        let coefScaleFactor = 2;
+        for (let k = 0; k < fb2.length; ++k) {
+          fb2[k] = coefScaleFactor * fb1[k];
+        }
+
+        let iir1;
+        let iir2;
+
+        should(function() {
+          iir1 = context.createIIRFilter(ff, fb1);
+        }, 'createIIRFilter with normalized coefficients').notThrow();
+
+        should(function() {
+          iir2 = context.createIIRFilter(ff, fb2);
+        }, 'createIIRFilter with unnormalized coefficients').notThrow();
+
+        // Create the graph.  The output of iir1 (normalized coefficients) is
+        // channel 0, and the output of iir2 (unnormalized coefficients), with
+        // appropriate scaling, is channel 1.
+        let merger = context.createChannelMerger(2);
+        source.connect(iir1);
+        source.connect(iir2);
+        iir1.connect(merger, 0, 0);
+        iir2.connect(gain);
+
+        // The gain for the gain node should be set to compensate for the
+        // scaling of the coefficients.  Since iir2 has scaled the coefficients
+        // by coefScaleFactor, the output is reduced by the same factor, so
+        // adjust the gain to scale the output of iir2 back up.
+        gain.gain.value = coefScaleFactor;
+        gain.connect(merger, 0, 1);
+
+        merger.connect(context.destination);
+
+        source.start();
+
+        // Rock and roll!
+
+        context.startRendering()
+            .then(function(result) {
+              // Find the max amplitude of the result, which should be near
+              // zero.
+              let iir1Data = result.getChannelData(0);
+              let iir2Data = result.getChannelData(1);
+
+              // Threshold isn't exactly zero because the arithmetic is done
+              // differently between the IIRFilterNode and the BiquadFilterNode.
+              should(
+                  iir2Data,
+                  'Output of IIR filter with unnormalized coefficients')
+                  .beCloseToArray(iir1Data, {absoluteThreshold: 2.1958e-38});
+            })
+            .then(() => task.done());
+      });
+
+      audit.define('one-zero', (task, should) => {
+        // Create a simple 1-zero filter and compare with the expected output.
+        let context = new OfflineAudioContext(1, testFrames, sampleRate);
+
+        // Use a simple impulse as the source
+        let buffer = context.createBuffer(1, 1, sampleRate);
+        buffer.getChannelData(0)[0] = 1;
+        let source = context.createBufferSource();
+        source.buffer = buffer;
+
+        // The filter is y(n) = 0.5*(x(n) + x(n-1)), a simple 2-point moving
+        // average.  This is rather arbitrary; keep it simple.
+
+        let iir = context.createIIRFilter([0.5, 0.5], [1]);
+
+        // Create the graph
+        source.connect(iir);
+        iir.connect(context.destination);
+
+        // Rock and roll!
+        source.start();
+
+        context.startRendering()
+            .then(function(result) {
+              let actual = result.getChannelData(0);
+              let expected = new Float64Array(testFrames);
+              // The filter is a simple 2-point moving average of an impulse, so
+              // the first two values are non-zero and the rest are zero.
+              expected[0] = 0.5;
+              expected[1] = 0.5;
+              should(actual, 'IIR 1-zero output').beCloseToArray(expected, {
+                absoluteThreshold: 0
+              });
+            })
+            .then(() => task.done());
+      });
+
+      audit.define('one-pole', (task, should) => {
+        // Create a simple 1-pole filter and compare with the expected output.
+
+        // The filter is y(n) + c*y(n-1)= x(n).  The analytical response is
+        // (-c)^n, so choose a suitable number of frames to run the test for
+        // where the output isn't flushed to zero.
+        let c = 0.9;
+        let eps = 1e-20;
+        let duration = Math.floor(Math.log(eps) / Math.log(Math.abs(c)));
+        let context = new OfflineAudioContext(1, duration, sampleRate);
+
+        // Use a simple impulse as the source
+        let buffer = context.createBuffer(1, 1, sampleRate);
+        buffer.getChannelData(0)[0] = 1;
+        let source = context.createBufferSource();
+        source.buffer = buffer;
+
+        let iir = context.createIIRFilter([1], [1, c]);
+
+        // Create the graph
+        source.connect(iir);
+        iir.connect(context.destination);
+
+        // Rock and roll!
+        source.start();
+
+        context.startRendering()
+            .then(function(result) {
+              let actual = result.getChannelData(0);
+              let expected = new Float64Array(actual.length);
+
+              // The filter is a simple 1-pole filter: y(n) = -c*y(n-k)+x(n),
+              // with an impulse as the input.
+              expected[0] = 1;
+              for (k = 1; k < testFrames; ++k) {
+                expected[k] = -c * expected[k - 1];
+              }
+
+              // Threshold isn't exactly zero due to round-off in the
+              // single-precision IIRFilterNode computations versus the
+              // double-precision Javascript computations.
+              should(actual, 'IIR 1-pole output').beCloseToArray(expected, {
+                absoluteThreshold: 2.7657e-8
+              });
+            })
+            .then(() => task.done());
+      });
+
+      // Return a function suitable for use as a defineTask function.  This
+      // function creates an IIRFilterNode equivalent to the specified
+      // BiquadFilterNode and compares the outputs.  The outputs from the two
+      // filters should be virtually identical.
+      function testWithBiquadFilter(filterType, errorThreshold, snrThreshold) {
+        return (task, should) => {
+          let context = new OfflineAudioContext(2, testFrames, sampleRate);
+
+          // Use a constant (step function) as the source
+          let buffer = createConstantBuffer(context, testFrames, 1);
+          let source = context.createBufferSource();
+          source.buffer = buffer;
+
+
+          // Create the biquad.  Choose some rather arbitrary values for Q and
+          // gain for the biquad so that the shelf filters aren't identical.
+          let biquad = context.createBiquadFilter();
+          biquad.type = filterType;
+          biquad.Q.value = 10;
+          biquad.gain.value = 10;
+
+          // Create the equivalent IIR Filter node by computing the coefficients
+          // of the given biquad filter type.
+          let nyquist = sampleRate / 2;
+          let coef = createFilter(
+              filterType, biquad.frequency.value / nyquist, biquad.Q.value,
+              biquad.gain.value);
+
+          let iir = context.createIIRFilter(
+              [coef.b0, coef.b1, coef.b2], [1, coef.a1, coef.a2]);
+
+          let merger = context.createChannelMerger(2);
+          // Create the graph
+          source.connect(biquad);
+          source.connect(iir);
+
+          biquad.connect(merger, 0, 0);
+          iir.connect(merger, 0, 1);
+
+          merger.connect(context.destination);
+
+          // Rock and roll!
+          source.start();
+
+          context.startRendering()
+              .then(function(result) {
+                // Find the max amplitude of the result, which should be near
+                // zero.
+                let expected = result.getChannelData(0);
+                let actual = result.getChannelData(1);
+
+                // On MacOSX, WebAudio uses an optimized Biquad implementation
+                // that is different from the implementation used for Linux and
+                // Windows.  This will cause the output to differ, even if the
+                // threshold passes.  Thus, only print out a very small number
+                // of elements of the array where we have tested that they are
+                // consistent.
+                should(actual, 'IIRFilter for Biquad ' + filterType)
+                    .beCloseToArray(expected, errorThreshold);
+
+                let snr = 10 * Math.log10(computeSNR(actual, expected));
+                should(snr, 'SNR for IIRFIlter for Biquad ' + filterType)
+                    .beGreaterThanOrEqualTo(snrThreshold);
+              })
+              .then(() => task.done());
+        };
+      }
+
+      // Thresholds here are experimentally determined.
+      let biquadTestConfigs = [
+        {
+          filterType: 'lowpass',
+          snrThreshold: 91.221,
+          errorThreshold: {relativeThreshold: 4.9834e-5}
+        },
+        {
+          filterType: 'highpass',
+          snrThreshold: 105.4590,
+          errorThreshold: {absoluteThreshold: 2.9e-6, relativeThreshold: 3e-5}
+        },
+        {
+          filterType: 'bandpass',
+          snrThreshold: 104.060,
+          errorThreshold: {absoluteThreshold: 2e-7, relativeThreshold: 8.7e-4}
+        },
+        {
+          filterType: 'notch',
+          snrThreshold: 91.312,
+          errorThreshold: {absoluteThreshold: 0, relativeThreshold: 4.22e-5}
+        },
+        {
+          filterType: 'allpass',
+          snrThreshold: 91.319,
+          errorThreshold: {absoluteThreshold: 0, relativeThreshold: 4.31e-5}
+        },
+        {
+          filterType: 'lowshelf',
+          snrThreshold: 90.609,
+          errorThreshold: {absoluteThreshold: 0, relativeThreshold: 2.98e-5}
+        },
+        {
+          filterType: 'highshelf',
+          snrThreshold: 103.159,
+          errorThreshold: {absoluteThreshold: 0, relativeThreshold: 1.24e-5}
+        },
+        {
+          filterType: 'peaking',
+          snrThreshold: 91.504,
+          errorThreshold: {absoluteThreshold: 0, relativeThreshold: 5.05e-5}
+        }
+      ];
+
+      // Create a set of tasks based on biquadTestConfigs.
+      for (k = 0; k < biquadTestConfigs.length; ++k) {
+        let config = biquadTestConfigs[k];
+        let name = k + ': ' + config.filterType;
+        audit.define(
+            name,
+            testWithBiquadFilter(
+                config.filterType, config.errorThreshold, config.snrThreshold));
+      }
+
+      audit.define('multi-channel', (task, should) => {
+        // Multi-channel test.  Create a biquad filter and the equivalent IIR
+        // filter.  Filter the same multichannel signal and compare the results.
+        let nChannels = 3;
+        let context =
+            new OfflineAudioContext(nChannels, testFrames, sampleRate);
+
+        // Create a set of oscillators as the multi-channel source.
+        let source = [];
+
+        for (k = 0; k < nChannels; ++k) {
+          source[k] = context.createOscillator();
+          source[k].type = 'sawtooth';
+          // The frequency of the oscillator is pretty arbitrary, but each
+          // oscillator should have a different frequency.
+          source[k].frequency.value = 100 + k * 100;
+        }
+
+        let merger = context.createChannelMerger(3);
+
+        let biquad = context.createBiquadFilter();
+
+        // Create the equivalent IIR Filter node.
+        let nyquist = sampleRate / 2;
+        let coef = createFilter(
+            biquad.type, biquad.frequency.value / nyquist, biquad.Q.value,
+            biquad.gain.value);
+        let fb = [1, coef.a1, coef.a2];
+        let ff = [coef.b0, coef.b1, coef.b2];
+
+        let iir = context.createIIRFilter(ff, fb);
+        // Gain node to compute the difference between the IIR and biquad
+        // filter.
+        let gain = context.createGain();
+        gain.gain.value = -1;
+
+        // Create the graph.
+        for (k = 0; k < nChannels; ++k)
+          source[k].connect(merger, 0, k);
+
+        merger.connect(biquad);
+        merger.connect(iir);
+        iir.connect(gain);
+        biquad.connect(context.destination);
+        gain.connect(context.destination);
+
+        for (k = 0; k < nChannels; ++k)
+          source[k].start();
+
+        context.startRendering()
+            .then(function(result) {
+              let errorThresholds = [3.7671e-5, 3.0071e-5, 2.6241e-5];
+
+              // Check the difference signal on each channel
+              for (channel = 0; channel < result.numberOfChannels; ++channel) {
+                // Find the max amplitude of the result, which should be near
+                // zero.
+                let data = result.getChannelData(channel);
+                let maxError =
+                    data.reduce(function(reducedValue, currentValue) {
+                      return Math.max(reducedValue, Math.abs(currentValue));
+                    });
+
+                should(
+                    maxError,
+                    'Max difference between IIR and Biquad on channel ' +
+                        channel)
+                    .beLessThanOrEqualTo(errorThresholds[channel]);
+              }
+
+            })
+            .then(() => task.done());
+      });
+
+      // Apply an IIRFilter to the given input signal.
+      //
+      // IIR filter in the time domain is
+      //
+      //   y[n] = sum(ff[k]*x[n-k], k, 0, M) - sum(fb[k]*y[n-k], k, 1, N)
+      //
+      function iirFilter(input, feedforward, feedback) {
+        // For simplicity, create an x buffer that contains the input, and a y
+        // buffer that contains the output.  Both of these buffers have an
+        // initial work space to implement the initial memory of the filter.
+        let workSize = Math.max(feedforward.length, feedback.length);
+        let x = new Float32Array(input.length + workSize);
+
+        // Float64 because we want to match the implementation that uses doubles
+        // to minimize roundoff.
+        let y = new Float64Array(input.length + workSize);
+
+        // Copy the input over.
+        for (let k = 0; k < input.length; ++k)
+          x[k + feedforward.length] = input[k];
+
+        // Run the filter
+        for (let n = 0; n < input.length; ++n) {
+          let index = n + workSize;
+          let yn = 0;
+          for (let k = 0; k < feedforward.length; ++k)
+            yn += feedforward[k] * x[index - k];
+          for (let k = 0; k < feedback.length; ++k)
+            yn -= feedback[k] * y[index - k];
+
+          y[index] = yn;
+        }
+
+        return y.slice(workSize).map(Math.fround);
+      }
+
+      // Cascade the two given biquad filters to create one IIR filter.
+      function cascadeBiquads(f1Coef, f2Coef) {
+        // The biquad filters are:
+        //
+        // f1 = (b10 + b11/z + b12/z^2)/(1 + a11/z + a12/z^2);
+        // f2 = (b20 + b21/z + b22/z^2)/(1 + a21/z + a22/z^2);
+        //
+        // To cascade them, multiply the two transforms together to get a fourth
+        // order IIR filter.
+
+        let numProduct = [
+          f1Coef.b0 * f2Coef.b0, f1Coef.b0 * f2Coef.b1 + f1Coef.b1 * f2Coef.b0,
+          f1Coef.b0 * f2Coef.b2 + f1Coef.b1 * f2Coef.b1 + f1Coef.b2 * f2Coef.b0,
+          f1Coef.b1 * f2Coef.b2 + f1Coef.b2 * f2Coef.b1, f1Coef.b2 * f2Coef.b2
+        ];
+
+        let denProduct = [
+          1, f2Coef.a1 + f1Coef.a1,
+          f2Coef.a2 + f1Coef.a1 * f2Coef.a1 + f1Coef.a2,
+          f1Coef.a1 * f2Coef.a2 + f1Coef.a2 * f2Coef.a1, f1Coef.a2 * f2Coef.a2
+        ];
+
+        return {
+          ff: numProduct, fb: denProduct
+        }
+      }
+
+      // Find the magnitude of the root of the quadratic that has the maximum
+      // magnitude.
+      //
+      // The quadratic is z^2 + a1 * z + a2 and we want the root z that has the
+      // largest magnitude.
+      function largestRootMagnitude(a1, a2) {
+        let discriminant = a1 * a1 - 4 * a2;
+        if (discriminant < 0) {
+          // Complex roots:  -a1/2 +/- i*sqrt(-d)/2.  Thus the magnitude of each
+          // root is the same and is sqrt(a1^2/4 + |d|/4)
+          let d = Math.sqrt(-discriminant);
+          return Math.hypot(a1 / 2, d / 2);
+        } else {
+          // Real roots
+          let d = Math.sqrt(discriminant);
+          return Math.max(Math.abs((-a1 + d) / 2), Math.abs((-a1 - d) / 2));
+        }
+      }
+
+      audit.define('4th-order-iir', (task, should) => {
+        // Cascade 2 lowpass biquad filters and compare that with the equivalent
+        // 4th order IIR filter.
+
+        let nyquist = sampleRate / 2;
+        // Compute the coefficients of a lowpass filter.
+
+        // First some preliminary stuff.  Compute the coefficients of the
+        // biquad.  This is used to figure out how frames to use in the test.
+        let biquadType = 'lowpass';
+        let biquadCutoff = 350;
+        let biquadQ = 5;
+        let biquadGain = 1;
+
+        let coef = createFilter(
+            biquadType, biquadCutoff / nyquist, biquadQ, biquadGain);
+
+        // Cascade the biquads together to create an equivalent IIR filter.
+        let cascade = cascadeBiquads(coef, coef);
+
+        // Since we're cascading two identical biquads, the root of denominator
+        // of the IIR filter is repeated, so the root of the denominator with
+        // the largest magnitude occurs twice.  The impulse response of the IIR
+        // filter will be roughly c*(r*r)^n at time n, where r is the root of
+        // largest magnitude.  This approximation gets better as n increases.
+        // We can use this to get a rough idea of when the response has died
+        // down to a small value.
+
+        // This is the value we will use to determine how many frames to render.
+        // Rendering too many is a waste of time and also makes it hard to
+        // compare the actual result to the expected because the magnitudes are
+        // so small that they could be mostly round-off noise.
+        //
+        // Find magnitude of the root with largest magnitude
+        let rootMagnitude = largestRootMagnitude(coef.a1, coef.a2);
+
+        // Find n such that |r|^(2*n) <= eps.  That is, n = log(eps)/(2*log(r)).
+        // Somewhat arbitrarily choose eps = 1e-20;
+        let eps = 1e-20;
+        let framesForTest =
+            Math.floor(Math.log(eps) / (2 * Math.log(rootMagnitude)));
+
+        // We're ready to create the graph for the test.  The offline context
+        // has two channels: channel 0 is the expected (cascaded biquad) result
+        // and channel 1 is the actual IIR filter result.
+        let context = new OfflineAudioContext(2, framesForTest, sampleRate);
+
+        // Use a simple impulse with a large (arbitrary) amplitude as the source
+        let amplitude = 1;
+        let buffer = context.createBuffer(1, testFrames, sampleRate);
+        buffer.getChannelData(0)[0] = amplitude;
+        let source = context.createBufferSource();
+        source.buffer = buffer;
+
+        // Create the two biquad filters.  Doesn't really matter what, but for
+        // simplicity we choose identical lowpass filters with the same
+        // parameters.
+        let biquad1 = context.createBiquadFilter();
+        biquad1.type = biquadType;
+        biquad1.frequency.value = biquadCutoff;
+        biquad1.Q.value = biquadQ;
+
+        let biquad2 = context.createBiquadFilter();
+        biquad2.type = biquadType;
+        biquad2.frequency.value = biquadCutoff;
+        biquad2.Q.value = biquadQ;
+
+        let iir = context.createIIRFilter(cascade.ff, cascade.fb);
+
+        // Create the merger to get the signals into multiple channels
+        let merger = context.createChannelMerger(2);
+
+        // Create the graph, filtering the source through two biquads.
+        source.connect(biquad1);
+        biquad1.connect(biquad2);
+        biquad2.connect(merger, 0, 0);
+
+        source.connect(iir);
+        iir.connect(merger, 0, 1);
+
+        merger.connect(context.destination);
+
+        // Now filter the source through the IIR filter.
+        let y = iirFilter(buffer.getChannelData(0), cascade.ff, cascade.fb);
+
+        // Rock and roll!
+        source.start();
+
+        context.startRendering()
+            .then(function(result) {
+              let expected = result.getChannelData(0);
+              let actual = result.getChannelData(1);
+
+              should(actual, '4-th order IIRFilter (biquad ref)')
+                  .beCloseToArray(expected, {
+                    // Thresholds experimentally determined.
+                    absoluteThreshold: 1.59e-7,
+                    relativeThreshold: 2.11e-5,
+                  });
+
+              let snr = 10 * Math.log10(computeSNR(actual, expected));
+              should(snr, 'SNR of 4-th order IIRFilter (biquad ref)')
+                  .beGreaterThanOrEqualTo(108.947);
+            })
+            .then(() => task.done());
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html b/webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html
new file mode 100644
index 0000000..383e2c6
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      distance-exponential.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/distance-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      audit.define(
+          {
+            label: 'test',
+            description: 'Exponential distance model for PannerNode'
+          },
+          (task, should) => {
+            // Create offline audio context.
+            context = new OfflineAudioContext(
+                2, sampleRate * renderLengthSeconds, sampleRate);
+
+            createTestAndRun(context, 'exponential', should)
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/distance-inverse.html b/webaudio/the-audio-api/the-pannernode-interface/distance-inverse.html
new file mode 100644
index 0000000..a4ff984
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/distance-inverse.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      distance-inverse.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/distance-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      audit.define('test', (task, should) => {
+        // Create offline audio context.
+        context = new OfflineAudioContext(
+            2, sampleRate * renderLengthSeconds, sampleRate);
+
+        createTestAndRun(context, 'inverse', should).then(() => task.done());
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/distance-linear.html b/webaudio/the-audio-api/the-pannernode-interface/distance-linear.html
new file mode 100644
index 0000000..812fea3
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/distance-linear.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      distance-linear.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/distance-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      audit.define(
+          {label: 'test', description: 'Linear distance model PannerNode'},
+          (task, should) => {
+            // Create offline audio context.
+            context = new OfflineAudioContext(
+                2, sampleRate * renderLengthSeconds, sampleRate);
+
+            createTestAndRun(context, 'linear', should).then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-automation-basic.html b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-basic.html
new file mode 100644
index 0000000..5c3df0e
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-basic.html
@@ -0,0 +1,298 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Basic PannerNode with Automation Position Properties
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/panner-formulas.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 48000;
+
+      // These tests are quite slow, so don't run for many frames.  256 frames
+      // should be enough to demonstrate that automations are working.
+      let renderFrames = 256;
+      let renderDuration = renderFrames / sampleRate;
+
+      let audit = Audit.createTaskRunner();
+
+      // Array of tests for setting the panner positions.  These tests basically
+      // verify that the position setters for the panner and listener are
+      // working correctly.
+      let testConfig = [
+        {
+          setter: 'positionX',
+        },
+        {
+          setter: 'positionY',
+        },
+        {
+          setter: 'positionZ',
+        }
+      ];
+
+      // Create tests for the panner position setters.  Both mono and steroe
+      // sources are tested.
+      for (let k = 0; k < testConfig.length; ++k) {
+        let config = testConfig[k];
+        // Function to create the test to define the test.
+        let tester = function(config, channelCount) {
+          return (task, should) => {
+            let nodes = createGraph(channelCount);
+            let {context, source, panner} = nodes;
+
+            let message = channelCount == 1 ? 'Mono' : 'Stereo';
+            message += ' panner.' + config.setter;
+
+            testPositionSetter(should, {
+              nodes: nodes,
+              pannerSetter: panner[config.setter],
+              message: message
+            }).then(() => task.done());
+          }
+        };
+
+        audit.define('Stereo panner.' + config.setter, tester(config, 2));
+        audit.define('Mono panner.' + config.setter, tester(config, 1));
+      }
+
+      // Create tests for the listener position setters.  Both mono and steroe
+      // sources are tested.
+      for (let k = 0; k < testConfig.length; ++k) {
+        let config = testConfig[k];
+        // Function to create the test to define the test.
+        let tester = function(config, channelCount) {
+          return (task, should) => {
+            let nodes = createGraph(channelCount);
+            let {context, source, panner} = nodes;
+
+            let message = channelCount == 1 ? 'Mono' : 'Stereo';
+            message += ' listener.' + config.setter;
+
+            // Some relatively arbitrary (non-default) position for the source
+            // location.
+            panner.setPosition(1, 0, 1);
+
+            testPositionSetter(should, {
+              nodes: nodes,
+              pannerSetter: context.listener[config.setter],
+              message: message
+            }).then(() => task.done());
+          }
+        };
+
+        audit.define('Stereo listener.' + config.setter, tester(config, 2));
+        audit.define('Mono listener.' + config.setter, tester(config, 1));
+      }
+
+      // Test setPosition method.
+      audit.define('setPosition', (task, should) => {
+        let {context, panner, source} = createGraph(2);
+
+        // Initialize source position (values don't really matter).
+        panner.setPosition(1, 1, 1);
+
+        // After some (unimportant) time, move the panner to a (any) new
+        // location.
+        let suspendFrame = 128;
+        context.suspend(suspendFrame / sampleRate)
+            .then(function() {
+              panner.setPosition(-100, 2000, 8000);
+            })
+            .then(context.resume.bind(context));
+
+        context.startRendering()
+            .then(function(resultBuffer) {
+              verifyPannerOutputChanged(
+                  should, resultBuffer,
+                  {message: 'setPosition', suspendFrame: suspendFrame});
+            })
+            .then(() => task.done());
+      });
+
+      audit.define('orientation setter', (task, should) => {
+        let {context, panner, source} = createGraph(2);
+
+        // For orientation to matter, we need to make the source directional,
+        // and also move away from the listener (because the default location is
+        // 0,0,0).
+        panner.setPosition(0, 0, 1);
+        panner.coneInnerAngle = 0;
+        panner.coneOuterAngle = 360;
+        panner.coneOuterGain = .001;
+
+        // After some (unimportant) time, change the panner orientation to a new
+        // orientation.  The only constraint is that the orientation changes
+        // from before.
+        let suspendFrame = 128;
+        context.suspend(suspendFrame / sampleRate)
+            .then(function() {
+              panner.orientationX.value = -100;
+              panner.orientationY.value = 2000;
+              panner.orientationZ.value = 8000;
+            })
+            .then(context.resume.bind(context));
+
+        context.startRendering()
+            .then(function(resultBuffer) {
+              verifyPannerOutputChanged(should, resultBuffer, {
+                message: 'panner.orientation{XYZ}',
+                suspendFrame: suspendFrame
+              });
+            })
+            .then(() => task.done());
+      });
+
+      audit.define('forward setter', (task, should) => {
+        let {context, panner, source} = createGraph(2);
+
+        // For orientation to matter, we need to make the source directional,
+        // and also move away from the listener (because the default location is
+        // 0,0,0).
+        panner.setPosition(0, 0, 1);
+        panner.coneInnerAngle = 0;
+        panner.coneOuterAngle = 360;
+        panner.coneOuterGain = .001;
+
+        // After some (unimportant) time, change the panner orientation to a new
+        // orientation.  The only constraint is that the orientation changes
+        // from before.
+        let suspendFrame = 128;
+        context.suspend(suspendFrame / sampleRate)
+            .then(function() {
+              context.listener.forwardX.value = -100;
+              context.listener.forwardY.value = 2000;
+              context.listener.forwardZ.value = 8000;
+            })
+            .then(context.resume.bind(context));
+
+        context.startRendering()
+            .then(function(resultBuffer) {
+              verifyPannerOutputChanged(should, resultBuffer, {
+                message: 'listener.forward{XYZ}',
+                suspendFrame: suspendFrame
+              });
+            })
+            .then(() => task.done());
+      });
+
+      audit.define('up setter', (task, should) => {
+        let {context, panner, source} = createGraph(2);
+
+        // For orientation to matter, we need to make the source directional,
+        // and also move away from the listener (because the default location is
+        // 0,0,0).
+        panner.setPosition(0, 0, 1);
+        panner.coneInnerAngle = 0;
+        panner.coneOuterAngle = 360;
+        panner.coneOuterGain = .001;
+        panner.setPosition(1, 0, 1);
+
+        // After some (unimportant) time, change the panner orientation to a new
+        // orientation.  The only constraint is that the orientation changes
+        // from before.
+        let suspendFrame = 128;
+        context.suspend(suspendFrame / sampleRate)
+            .then(function() {
+              context.listener.upX.value = 100;
+              context.listener.upY.value = 100;
+              context.listener.upZ.value = 100;
+              ;
+            })
+            .then(context.resume.bind(context));
+
+        context.startRendering()
+            .then(function(resultBuffer) {
+              verifyPannerOutputChanged(
+                  should, resultBuffer,
+                  {message: 'listener.up{XYZ}', suspendFrame: suspendFrame});
+            })
+            .then(() => task.done());
+      });
+
+      audit.run();
+
+      function createGraph(channelCount) {
+        let context = new OfflineAudioContext(2, renderFrames, sampleRate);
+        let panner = context.createPanner();
+        let source = context.createBufferSource();
+        source.buffer =
+            createConstantBuffer(context, 1, channelCount == 1 ? 1 : [1, 2]);
+        source.loop = true;
+
+        source.connect(panner);
+        panner.connect(context.destination);
+
+        source.start();
+        return {context: context, source: source, panner: panner};
+      }
+
+      function testPositionSetter(should, options) {
+        let {nodes, pannerSetter, message} = options;
+
+        let {context, source, panner} = nodes;
+
+        // Set panner x position. (Value doesn't matter);
+        pannerSetter.value = 1;
+
+        // Wait a bit and set a new position.  (Actual time and position doesn't
+        // matter).
+        let suspendFrame = 128;
+        context.suspend(suspendFrame / sampleRate)
+            .then(function() {
+              pannerSetter.value = 10000;
+            })
+            .then(context.resume.bind(context));
+
+        return context.startRendering().then(function(resultBuffer) {
+          verifyPannerOutputChanged(
+              should, resultBuffer,
+              {message: message, suspendFrame: suspendFrame});
+        });
+      }
+
+      function verifyPannerOutputChanged(should, resultBuffer, options) {
+        let {message, suspendFrame} = options;
+        // Verify that the first part of output is constant. (Doesn't matter
+        // what.)
+        let data0 = resultBuffer.getChannelData(0);
+        let data1 = resultBuffer.getChannelData(1);
+
+        let middle = '[0, ' + suspendFrame + ') ';
+        should(
+            data0.slice(0, suspendFrame),
+            message + '.value frame ' + middle + 'channel 0')
+            .beConstantValueOf(data0[0]);
+        should(
+            data1.slice(0, suspendFrame),
+            message + '.value frame ' + middle + 'channel 1')
+            .beConstantValueOf(data1[0]);
+
+        // The rest after suspendTime should be constant and different from the
+        // first part.
+        middle = '[' + suspendFrame + ', ' + renderFrames + ') ';
+        should(
+            data0.slice(suspendFrame),
+            message + '.value frame ' + middle + 'channel 0')
+            .beConstantValueOf(data0[suspendFrame]);
+        should(
+            data1.slice(suspendFrame),
+            message + '.value frame ' + middle + 'channel 1')
+            .beConstantValueOf(data1[suspendFrame]);
+        should(
+            data0[suspendFrame],
+            message + ': Output at frame ' + suspendFrame + ' channel 0')
+            .notBeEqualTo(data0[0]);
+        should(
+            data1[suspendFrame],
+            message + ': Output at frame ' + suspendFrame + ' channel 1')
+            .notBeEqualTo(data1[0]);
+      }
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-automation-equalpower-stereo.html b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-equalpower-stereo.html
new file mode 100644
index 0000000..7afc9c2
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-equalpower-stereo.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      panner-automation-equalpower-stereo.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/panner-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      // To test the panner, we create a number of panner nodes
+      // equally spaced on a semicircle at unit distance.  The
+      // semicircle covers the azimuth range from -90 to 90 deg,
+      // covering full left to full right.  Each source is an impulse
+      // turning at a different time and we check that the rendered
+      // impulse has the expected gain.
+      audit.define(
+          {
+            label: 'test',
+            description:
+                'Equal-power panner model of AudioPannerNode with stereo source',
+          },
+          (task, should) => {
+            // Create offline audio context.
+            context = new OfflineAudioContext(
+                2, sampleRate * renderLengthSeconds, sampleRate);
+
+            createTestAndRun(
+                context, should, nodesToCreate, 2,
+                function(panner, x, y, z) {
+                  panner.positionX.value = x;
+                  panner.positionY.value = y;
+                  panner.positionZ.value = z;
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-automation-position.html b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-position.html
new file mode 100644
index 0000000..8e09e86
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-automation-position.html
@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Automation of PannerNode Positions
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/panner-formulas.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 48000;
+      // These tests are quite slow, so don't run for many frames.  256 frames
+      // should be enough to demonstrate that automations are working.
+      let renderFrames = 256;
+      let renderDuration = renderFrames / sampleRate;
+
+      let context;
+      let panner;
+
+      let audit = Audit.createTaskRunner();
+
+      // Set of tests for the panner node with automations applied to the
+      // position of the source.
+      let testConfigs = [
+        {
+          // Distance model parameters for the panner
+          distanceModel: {model: 'inverse', rolloff: 1},
+          // Initial location of the source
+          startPosition: [0, 0, 1],
+          // Final position of the source.  For this test, we only want to move
+          // on the z axis which
+          // doesn't change the azimuth angle.
+          endPosition: [0, 0, 10000],
+        },
+        {
+          distanceModel: {model: 'inverse', rolloff: 1},
+          startPosition: [0, 0, 1],
+          // An essentially random end position, but it should be such that
+          // azimuth angle changes as
+          // we move from the start to the end.
+          endPosition: [20000, 30000, 10000],
+          errorThreshold: [
+            {
+              // Error threshold for 1-channel case
+              relativeThreshold: 4.8124e-7
+            },
+            {
+              // Error threshold for 2-channel case
+              relativeThreshold: 4.3267e-7
+            }
+          ],
+        },
+        {
+          distanceModel: {model: 'exponential', rolloff: 1.5},
+          startPosition: [0, 0, 1],
+          endPosition: [20000, 30000, 10000],
+          errorThreshold:
+              [{relativeThreshold: 5.0783e-7}, {relativeThreshold: 5.2180e-7}]
+        },
+        {
+          distanceModel: {model: 'linear', rolloff: 1},
+          startPosition: [0, 0, 1],
+          endPosition: [20000, 30000, 10000],
+          errorThreshold: [
+            {relativeThreshold: 6.5324e-6}, {relativeThreshold: 6.5756e-6}
+          ]
+        }
+      ];
+
+      for (let k = 0; k < testConfigs.length; ++k) {
+        let config = testConfigs[k];
+        let tester = function(c, channelCount) {
+          return (task, should) => {
+            runTest(should, c, channelCount).then(() => task.done());
+          }
+        };
+
+        let baseTestName = config.distanceModel.model +
+            ' rolloff: ' + config.distanceModel.rolloff;
+
+        // Define tasks for both 1-channel and 2-channel
+        audit.define(k + ': 1-channel ' + baseTestName, tester(config, 1));
+        audit.define(k + ': 2-channel ' + baseTestName, tester(config, 2));
+      }
+
+      audit.run();
+
+      function runTest(should, options, channelCount) {
+        // Output has 5 channels: channels 0 and 1 are for the stereo output of
+        // the panner node. Channels 2-5 are the for automation of the x,y,z
+        // coordinate so that we have actual coordinates used for the panner
+        // automation.
+        context = new OfflineAudioContext(5, renderFrames, sampleRate);
+
+        // Stereo source for the panner.
+        let source = context.createBufferSource();
+        source.buffer = createConstantBuffer(
+            context, renderFrames, channelCount == 1 ? 1 : [1, 2]);
+
+        panner = context.createPanner();
+        panner.distanceModel = options.distanceModel.model;
+        panner.rolloffFactor = options.distanceModel.rolloff;
+        panner.panningModel = 'equalpower';
+
+        // Source and gain node for the z-coordinate calculation.
+        let dist = context.createBufferSource();
+        dist.buffer = createConstantBuffer(context, 1, 1);
+        dist.loop = true;
+        let gainX = context.createGain();
+        let gainY = context.createGain();
+        let gainZ = context.createGain();
+        dist.connect(gainX);
+        dist.connect(gainY);
+        dist.connect(gainZ);
+
+        // Set the gain automation to match the z-coordinate automation of the
+        // panner.
+
+        // End the automation some time before the end of the rendering so we
+        // can verify that automation has the correct end time and value.
+        let endAutomationTime = 0.75 * renderDuration;
+
+        gainX.gain.setValueAtTime(options.startPosition[0], 0);
+        gainX.gain.linearRampToValueAtTime(
+            options.endPosition[0], endAutomationTime);
+        gainY.gain.setValueAtTime(options.startPosition[1], 0);
+        gainY.gain.linearRampToValueAtTime(
+            options.endPosition[1], endAutomationTime);
+        gainZ.gain.setValueAtTime(options.startPosition[2], 0);
+        gainZ.gain.linearRampToValueAtTime(
+            options.endPosition[2], endAutomationTime);
+
+        dist.start();
+
+        // Splitter and merger to map the panner output and the z-coordinate
+        // automation to the correct channels in the destination.
+        let splitter = context.createChannelSplitter(2);
+        let merger = context.createChannelMerger(5);
+
+        source.connect(panner);
+        // Split the output of the panner to separate channels
+        panner.connect(splitter);
+
+        // Merge the panner outputs and the z-coordinate output to the correct
+        // destination channels.
+        splitter.connect(merger, 0, 0);
+        splitter.connect(merger, 1, 1);
+        gainX.connect(merger, 0, 2);
+        gainY.connect(merger, 0, 3);
+        gainZ.connect(merger, 0, 4);
+
+        merger.connect(context.destination);
+
+        // Initialize starting point of the panner.
+        panner.positionX.setValueAtTime(options.startPosition[0], 0);
+        panner.positionY.setValueAtTime(options.startPosition[1], 0);
+        panner.positionZ.setValueAtTime(options.startPosition[2], 0);
+
+        // Automate z coordinate to move away from the listener
+        panner.positionX.linearRampToValueAtTime(
+            options.endPosition[0], 0.75 * renderDuration);
+        panner.positionY.linearRampToValueAtTime(
+            options.endPosition[1], 0.75 * renderDuration);
+        panner.positionZ.linearRampToValueAtTime(
+            options.endPosition[2], 0.75 * renderDuration);
+
+        source.start();
+
+        // Go!
+        return context.startRendering().then(function(renderedBuffer) {
+          // Get the panner outputs
+          let data0 = renderedBuffer.getChannelData(0);
+          let data1 = renderedBuffer.getChannelData(1);
+          let xcoord = renderedBuffer.getChannelData(2);
+          let ycoord = renderedBuffer.getChannelData(3);
+          let zcoord = renderedBuffer.getChannelData(4);
+
+          // We're doing a linear ramp on the Z axis with the equalpower panner,
+          // so the equalpower panning gain remains constant.  We only need to
+          // model the distance effect.
+
+          // Compute the distance gain
+          let distanceGain = new Float32Array(xcoord.length);
+          ;
+
+          if (panner.distanceModel === 'inverse') {
+            for (let k = 0; k < distanceGain.length; ++k) {
+              distanceGain[k] =
+                  inverseDistance(panner, xcoord[k], ycoord[k], zcoord[k])
+            }
+          } else if (panner.distanceModel === 'linear') {
+            for (let k = 0; k < distanceGain.length; ++k) {
+              distanceGain[k] =
+                  linearDistance(panner, xcoord[k], ycoord[k], zcoord[k])
+            }
+          } else if (panner.distanceModel === 'exponential') {
+            for (let k = 0; k < distanceGain.length; ++k) {
+              distanceGain[k] =
+                  exponentialDistance(panner, xcoord[k], ycoord[k], zcoord[k])
+            }
+          }
+
+          // Compute the expected result.  Since we're on the z-axis, the left
+          // and right channels pass through the equalpower panner unchanged.
+          // Only need to apply the distance gain.
+          let buffer0 = source.buffer.getChannelData(0);
+          let buffer1 =
+              channelCount == 2 ? source.buffer.getChannelData(1) : buffer0;
+
+          let azimuth = new Float32Array(buffer0.length);
+
+          for (let k = 0; k < data0.length; ++k) {
+            azimuth[k] = calculateAzimuth(
+                [xcoord[k], ycoord[k], zcoord[k]],
+                [
+                  context.listener.positionX.value,
+                  context.listener.positionY.value,
+                  context.listener.positionZ.value
+                ],
+                [
+                  context.listener.forwardX.value,
+                  context.listener.forwardY.value,
+                  context.listener.forwardZ.value
+                ],
+                [
+                  context.listener.upX.value, context.listener.upY.value,
+                  context.listener.upZ.value
+                ]);
+          }
+
+          let expected = applyPanner(azimuth, buffer0, buffer1, channelCount);
+          let expected0 = expected.left;
+          let expected1 = expected.right;
+
+          for (let k = 0; k < expected0.length; ++k) {
+            expected0[k] *= distanceGain[k];
+            expected1[k] *= distanceGain[k];
+          }
+
+          let info = options.distanceModel.model +
+              ', rolloff: ' + options.distanceModel.rolloff;
+          let prefix = channelCount + '-channel ' +
+              '[' + options.startPosition[0] + ', ' + options.startPosition[1] +
+              ', ' + options.startPosition[2] + '] -> [' +
+              options.endPosition[0] + ', ' + options.endPosition[1] + ', ' +
+              options.endPosition[2] + ']: ';
+
+          let errorThreshold = 0;
+
+          if (options.errorThreshold)
+            errorThreshold = options.errorThreshold[channelCount - 1]
+
+            should(data0, prefix + 'distanceModel: ' + info + ', left channel')
+                .beCloseToArray(expected0, {absoluteThreshold: errorThreshold});
+          should(data1, prefix + 'distanceModel: ' + info + ', right channel')
+              .beCloseToArray(expected1, {absoluteThreshold: errorThreshold});
+        });
+      }
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html b/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html
new file mode 100644
index 0000000..dae58c9
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html
@@ -0,0 +1,233 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Clamping of Distance for PannerNode
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      // Arbitrary sample rate and render length.
+      let sampleRate = 48000;
+      let renderFrames = 128;
+
+      let audit = Audit.createTaskRunner();
+
+      audit.define('ref-distance-error', (task, should) => {
+        testDistanceLimits(should, {name: 'refDistance', isZeroAllowed: true});
+        task.done();
+      });
+
+      audit.define('max-distance-error', (task, should) => {
+        testDistanceLimits(should, {name: 'maxDistance', isZeroAllowed: false});
+        task.done();
+      });
+
+      function testDistanceLimits(should, options) {
+        // Verify that exceptions are thrown for invalid values of refDistance.
+        let context = new OfflineAudioContext(1, renderFrames, sampleRate);
+
+        let attrName = options.name;
+        let prefix = 'new PannerNode(c, {' + attrName + ': ';
+
+        should(function() {
+          let nodeOptions = {};
+          nodeOptions[attrName] = -1;
+          new PannerNode(context, nodeOptions);
+        }, prefix + '-1})').throw('RangeError');
+
+        if (options.isZeroAllowed) {
+          should(function() {
+            let nodeOptions = {};
+            nodeOptions[attrName] = 0;
+            new PannerNode(context, nodeOptions);
+          }, prefix + '0})').notThrow();
+        } else {
+          should(function() {
+            let nodeOptions = {};
+            nodeOptions[attrName] = 0;
+            new PannerNode(context, nodeOptions);
+          }, prefix + '0})').throw('RangeError');
+        }
+
+        // The smallest representable positive single float.
+        let leastPositiveDoubleFloat = 4.9406564584124654e-324;
+
+        should(function() {
+          let nodeOptions = {};
+          nodeOptions[attrName] = leastPositiveDoubleFloat;
+          new PannerNode(context, nodeOptions);
+        }, prefix + leastPositiveDoubleFloat + '})').notThrow();
+
+        prefix = 'panner.' + attrName + ' = ';
+        panner = new PannerNode(context);
+        should(function() {
+          panner[attrName] = -1;
+        }, prefix + '-1').throw('RangeError');
+
+        if (options.isZeroAllowed) {
+          should(function() {
+            panner[attrName] = 0;
+          }, prefix + '0').notThrow();
+        } else {
+          should(function() {
+            panner[attrName] = 0;
+          }, prefix + '0').throw('RangeError');
+        }
+
+        should(function() {
+          panner[attrName] = leastPositiveDoubleFloat;
+        }, prefix + leastPositiveDoubleFloat).notThrow();
+      }
+
+      audit.define('min-distance', (task, should) => {
+        // Test clamping of panner distance to refDistance for all of the
+        // distance models.  The actual distance is arbitrary as long as it's
+        // less than refDistance.  We test default and non-default values for
+        // the panner's refDistance and maxDistance.
+        // correctly.
+        Promise
+            .all([
+              runTest(should, {
+                distance: 0.01,
+                distanceModel: 'linear',
+              }),
+              runTest(should, {
+                distance: 0.01,
+                distanceModel: 'exponential',
+              }),
+              runTest(should, {
+                distance: 0.01,
+                distanceModel: 'inverse',
+              }),
+              runTest(should, {
+                distance: 2,
+                distanceModel: 'linear',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+              runTest(should, {
+                distance: 2,
+                distanceModel: 'exponential',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+              runTest(should, {
+                distance: 2,
+                distanceModel: 'inverse',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+            ])
+            .then(() => task.done());
+      });
+
+      audit.define('max-distance', (task, should) => {
+        // Like the "min-distance" task, but for clamping to the max
+        // distance. The actual distance is again arbitrary as long as it is
+        // greater than maxDistance.
+        Promise
+            .all([
+              runTest(should, {
+                distance: 20000,
+                distanceModel: 'linear',
+              }),
+              runTest(should, {
+                distance: 21000,
+                distanceModel: 'exponential',
+              }),
+              runTest(should, {
+                distance: 23000,
+                distanceModel: 'inverse',
+              }),
+              runTest(should, {
+                distance: 5000,
+                distanceModel: 'linear',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+              runTest(should, {
+                distance: 5000,
+                distanceModel: 'exponential',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+              runTest(should, {
+                distance: 5000,
+                distanceModel: 'inverse',
+                maxDistance: 1000,
+                refDistance: 10,
+              }),
+            ])
+            .then(() => task.done());
+      });
+
+      function runTest(should, options) {
+        let context = new OfflineAudioContext(2, renderFrames, sampleRate);
+        let src = new OscillatorNode(context, {
+          type: 'sawtooth',
+          frequency: 20 * 440,
+        });
+
+        // Set panner options.  Use a non-default rolloffFactor so that the
+        // various distance models look distinctly different.
+        let pannerOptions = {};
+        Object.assign(pannerOptions, options, {rolloffFactor: 0.5});
+
+        let pannerRef = new PannerNode(context, pannerOptions);
+        let pannerTest = new PannerNode(context, pannerOptions);
+
+        // Split the panner output so we can grab just one of the output
+        // channels.
+        let splitRef = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+        let splitTest = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+
+        // Merge the panner outputs back into one stereo stream for the
+        // destination.
+        let merger = new ChannelMergerNode(context, {numberOfInputs: 2});
+
+        src.connect(pannerTest).connect(splitTest).connect(merger, 0, 0);
+        src.connect(pannerRef).connect(splitRef).connect(merger, 0, 1);
+
+        merger.connect(context.destination);
+
+        // Move the panner some distance away. Arbitrarily select the x
+        // direction.  For the reference panner, manually clamp the distance.
+        // All models clamp the distance to a minimum of refDistance.  Only the
+        // linear model also clamps to a maximum of maxDistance.
+        let xRef = Math.max(options.distance, pannerRef.refDistance);
+
+        if (pannerRef.distanceModel === 'linear') {
+          xRef = Math.min(xRef, pannerRef.maxDistance);
+        }
+
+        let xTest = options.distance;
+
+        pannerRef.positionZ.setValueAtTime(xRef, 0);
+        pannerTest.positionZ.setValueAtTime(xTest, 0);
+
+        src.start();
+
+        return context.startRendering().then(function(resultBuffer) {
+          let actual = resultBuffer.getChannelData(0);
+          let expected = resultBuffer.getChannelData(1);
+
+          should(
+              xTest < pannerRef.refDistance || xTest > pannerRef.maxDistance,
+              'Model: ' + options.distanceModel + ': Distance (' + xTest +
+                  ') is outside the range [' + pannerRef.refDistance + ', ' +
+                  pannerRef.maxDistance + ']')
+              .beEqualTo(true);
+          should(actual, 'Test panner output ' + JSON.stringify(options))
+              .beEqualToArray(expected);
+        });
+      }
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower-stereo.html b/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower-stereo.html
new file mode 100644
index 0000000..2a0225b
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower-stereo.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      panner-equalpower-stereo.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/panner-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      // To test the panner, we create a number of panner nodes
+      // equally spaced on a semicircle at unit distance.  The
+      // semicircle covers the azimuth range from -90 to 90 deg,
+      // covering full left to full right.  Each source is an impulse
+      // turning at a different time and we check that the rendered
+      // impulse has the expected gain.
+      audit.define(
+          {
+            label: 'test',
+            description:
+                'Equal-power panner model of AudioPannerNode with stereo source'
+          },
+          (task, should) => {
+            context = new OfflineAudioContext(
+                2, sampleRate * renderLengthSeconds, sampleRate);
+
+            createTestAndRun(
+                context, should, nodesToCreate, 2,
+                function(panner, x, y, z) {
+                  panner.setPosition(x, y, z);
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower.html b/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower.html
new file mode 100644
index 0000000..3ff21b6
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-equalpower.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      panner-equalpower.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+    <script src="../../resources/panner-model-testing.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      // To test the panner, we create a number of panner nodes
+      // equally spaced on a semicircle at unit distance.  The
+      // semicircle covers the azimuth range from -90 to 90 deg,
+      // covering full left to full right.  Each source is an impulse
+      // turning at a different time and we check that the rendered
+      // impulse has the expected gain.
+      audit.define(
+          {
+            label: 'test',
+            description: 'Equal-power panner model of AudioPannerNode',
+          },
+          (task, should) => {
+            // Create offline audio context.
+            context = new OfflineAudioContext(
+                2, sampleRate * renderLengthSeconds, sampleRate);
+
+            createTestAndRun(
+                context, should, nodesToCreate, 1,
+                function(panner, x, y, z) {
+                  panner.setPosition(x, y, z);
+                })
+                .then(() => task.done());
+            ;
+          });
+
+      // Test that a mono source plays out on both the left and right channels
+      // when the source and listener positions are the same.
+      audit.define(
+          {
+            label: 'mono source=listener',
+            description: 'Source and listener at the same position'
+          },
+          (task, should) => {
+            // Must be stereo to verify output and only need a short duration
+            let context =
+                new OfflineAudioContext(2, 0.25 * sampleRate, sampleRate);
+
+            // Arbitrary position for source and listener.  Just so we don't use
+            // defaults positions.
+            let x = 1;
+            let y = 2;
+            let z = 3;
+
+            context.listener.setPosition(x, y, z);
+
+            let src = new OscillatorNode(context);
+            let panner = new PannerNode(context, {
+              panningModel: 'equalpower',
+              positionX: x,
+              positionY: y,
+              positionZ: z
+            });
+
+            src.connect(panner).connect(context.destination);
+
+            src.start();
+
+            context.startRendering()
+                .then(renderedBuffer => {
+                  // Verify that both channels have the same data because they
+                  // should when the source and listener are at the same
+                  // position
+                  let c0 = renderedBuffer.getChannelData(0);
+                  let c1 = renderedBuffer.getChannelData(1);
+                  should(c0, 'Mono: Left and right channels').beEqualToArray(c1);
+                })
+                .then(() => task.done());
+          });
+
+      // Test that a stereo source plays out on both the left and right channels
+      // when the source and listener positions are the same.
+      audit.define(
+          {
+            label: 'stereo source=listener',
+            description: 'Source and listener at the same position'
+          },
+          (task, should) => {
+            // Must be stereo to verify output and only need a short duration.
+            let context =
+                new OfflineAudioContext(2, 0.25 * sampleRate, sampleRate);
+
+            // Arbitrary position for source and listener.  Just so we don't use
+            // defaults positions.
+            let x = 1;
+            let y = 2;
+            let z = 3;
+
+            context.listener.setPosition(x, y, z);
+
+            let src = new OscillatorNode(context);
+            let merger = new ChannelMergerNode(context, {numberOfInputs: 2});
+            let panner = new PannerNode(context, {
+              panningModel: 'equalpower',
+              positionX: x,
+              positionY: y,
+              positionZ: z
+            });
+
+            // Make the oscillator a stereo signal (with identical signals on
+            // each channel).
+            src.connect(merger, 0, 0);
+            src.connect(merger, 0, 1);
+
+            merger.connect(panner).connect(context.destination);
+
+            src.start();
+
+            context.startRendering()
+                .then(renderedBuffer => {
+                  // Verify that both channels have the same data because they
+                  // should when the source and listener are at the same
+                  // position.
+                  let c0 = renderedBuffer.getChannelData(0);
+                  let c1 = renderedBuffer.getChannelData(1);
+                  should(c0, 'Stereo: Left and right channels').beEqualToArray(c1);
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/panner-rolloff-clamping.html b/webaudio/the-audio-api/the-pannernode-interface/panner-rolloff-clamping.html
new file mode 100644
index 0000000..e1519f8
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/panner-rolloff-clamping.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test Clamping of PannerNode rolloffFactor
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      // Fairly arbitrary sample rate and render frames.
+      let sampleRate = 16000;
+      let renderFrames = 2048;
+
+      let audit = Audit.createTaskRunner();
+
+      audit.define('linear-clamp-low', (task, should) => {
+        runTest(should, {
+          distanceModel: 'linear',
+          // Fairly arbitrary value outside the nominal range
+          rolloffFactor: -1,
+          clampedRolloff: 0
+        }).then(() => task.done());
+      });
+
+      audit.define('linear-clamp-high', (task, should) => {
+        runTest(should, {
+          distanceModel: 'linear',
+          // Fairly arbitrary value outside the nominal range
+          rolloffFactor: 2,
+          clampedRolloff: 1
+        }).then(() => task.done());
+      });
+
+      audit.define('inverse-clamp', (task, should) => {
+        runTest(should, {
+          distanceModel: 'inverse',
+          // Fairly arbitrary value outside the nominal range
+          rolloffFactor: -1,
+          clampedRolloff: 0
+        }).then(() => task.done());
+      });
+
+      audit.define('exponential-clamp', (task, should) => {
+        runTest(should, {
+          distanceModel: 'exponential',
+          // Fairly arbitrary value outside the nominal range
+          rolloffFactor: -2,
+          clampedRolloff: 0
+        }).then(() => task.done());
+      });
+
+      // Test clamping of the rolloffFactor.  The test is done by comparing the
+      // output of a panner with the rolloffFactor set outside the nominal range
+      // against the output of a panner with the rolloffFactor clamped to the
+      // nominal range.  The outputs should be the same.
+      //
+      // The |options| dictionary should contain the members
+      //   distanceModel  - The distance model to use for the panners
+      //   rolloffFactor  - The desired rolloffFactor.  Should be outside the
+      //                    nominal range of the distance model.
+      //   clampedRolloff - The rolloffFactor (above) clamped to the nominal
+      //                    range for the given distance model.
+      function runTest(should, options) {
+        // Offline context with two channels.  The first channel is the panner
+        // node under test.  The second channel is the reference panner node.
+        let context = new OfflineAudioContext(2, renderFrames, sampleRate);
+
+        // The source for the panner nodes.  This is fairly arbitrary.
+        let src = new OscillatorNode(context, {type: 'sawtooth'});
+
+        // Create the test panner with the specified rolloff factor.  The
+        // position is fairly arbitrary, but something that is not the default
+        // is good to show the distance model had some effect.
+        let pannerTest = new PannerNode(context, {
+          rolloffFactor: options.rolloffFactor,
+          distanceModel: options.distanceModel,
+          positionX: 5000
+        });
+
+        // Create the reference panner with the rolloff factor clamped to the
+        // appropriate limit.
+        let pannerRef = new PannerNode(context, {
+          rolloffFactor: options.clampedRolloff,
+          distanceModel: options.distanceModel,
+          positionX: 5000
+        });
+
+
+        // Connect the source to the panners to the destination appropriately.
+        let merger = new ChannelMergerNode(context, {numberOfInputs: 2});
+
+
+        src.connect(pannerTest).connect(merger, 0, 0);
+        src.connect(pannerRef).connect(merger, 0, 1);
+
+        merger.connect(context.destination);
+
+        src.start();
+
+        return context.startRendering().then(function(resultBuffer) {
+          // The two channels should be the same due to the clamping.  Verify
+          // that they are the same.
+          let actual = resultBuffer.getChannelData(0);
+          let expected = resultBuffer.getChannelData(1);
+
+          let message = 'Panner distanceModel: "' + options.distanceModel +
+              '", rolloffFactor: ' + options.rolloffFactor;
+
+          should(actual, message).beEqualToArray(expected);
+        });
+      }
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-pannernode-interface/pannernode-basic.html b/webaudio/the-audio-api/the-pannernode-interface/pannernode-basic.html
new file mode 100644
index 0000000..32402f5
--- /dev/null
+++ b/webaudio/the-audio-api/the-pannernode-interface/pannernode-basic.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      pannernode-basic.html
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit-util.js"></script>
+    <script src="../../resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let context;
+      let panner;
+      let audit = Audit.createTaskRunner();
+
+      audit.define('initialize', (task, should) => {
+        should(() => {
+          context = new AudioContext();
+          panner = context.createPanner();
+        }, 'Initialize context and panner').notThrow();
+        task.done();
+      });
+
+      audit.define('basic', (task, should) => {
+        should(panner.numberOfInputs, 'panner.numberOfInputs').beEqualTo(1);
+        should(panner.numberOfOutputs, 'panner.numberOfOutputs').beEqualTo(1);
+        should(panner.refDistance, 'panner.refDistance').beEqualTo(1);
+        panner.refDistance = 270.5;
+        should(panner.refDistance, 'panner.refDistance = 270.5')
+            .beEqualTo(270.5);
+        should(panner.maxDistance, 'panner.maxDistance').beEqualTo(10000);
+        panner.maxDistance = 100.5;
+        should(panner.maxDistance, 'panner.maxDistance = 100.5')
+            .beEqualTo(100.5);
+        should(panner.rolloffFactor, 'panner.rolloffFactor').beEqualTo(1);
+        panner.rolloffFactor = 0.75;
+        should(panner.rolloffFactor, 'panner.rolloffFactor = 0.75')
+            .beEqualTo(0.75);
+        should(panner.coneInnerAngle, 'panner.coneInnerAngle').beEqualTo(360);
+        panner.coneInnerAngle = 240.5;
+        should(panner.coneInnerAngle, 'panner.coneInnerAngle = 240.5')
+            .beEqualTo(240.5);
+        should(panner.coneOuterAngle, 'panner.coneOuterAngle').beEqualTo(360);
+        panner.coneOuterAngle = 166.5;
+        should(panner.coneOuterAngle, 'panner.coneOuterAngle = 166.5')
+            .beEqualTo(166.5);
+        should(panner.coneOuterGain, 'panner.coneOuterGain').beEqualTo(0);
+        panner.coneOuterGain = 0.25;
+        should(panner.coneOuterGain, 'panner.coneOuterGain = 0.25')
+            .beEqualTo(0.25);
+        should(panner.panningModel, 'panner.panningModel')
+            .beEqualTo('equalpower');
+        should(panner.distanceModel)
+            .beEqualTo('inverse', 'panner.distanceModel');
+
+        should(panner.positionX.value, 'panner.positionX').beEqualTo(0);
+        should(panner.positionY.value, 'panner.positionY').beEqualTo(0);
+        should(panner.positionZ.value, 'panner.positionZ').beEqualTo(0);
+        should(panner.orientationX.value, 'panner.orientationX').beEqualTo(1);
+        should(panner.orientationY.value, 'panner.orientationY').beEqualTo(0);
+        should(panner.orientationZ.value, 'panner.orientationZ').beEqualTo(0);
+
+        task.done();
+      });
+
+      audit.define('listener', (task, should) => {
+        should(context.listener.positionX.value, 'listener.positionX')
+            .beEqualTo(0);
+        should(context.listener.positionY.value, 'listener.positionY')
+            .beEqualTo(0);
+        should(context.listener.positionZ.value, 'listener.positionZ')
+            .beEqualTo(0);
+        should(context.listener.forwardX.value, 'listener.forwardX')
+            .beEqualTo(0);
+        should(context.listener.forwardY.value, 'listener.forwardY')
+            .beEqualTo(0);
+        should(context.listener.forwardZ.value, 'listener.forwardZ')
+            .beEqualTo(-1);
+        should(context.listener.upX.value, 'listener.upX').beEqualTo(0);
+        should(context.listener.upY.value, 'listener.upY').beEqualTo(1);
+        should(context.listener.upZ.value, 'listener.upZ').beEqualTo(0);
+
+        task.done();
+      });
+
+      audit.define('panning models', (task, should) => {
+        // Check that the .panningModel attribute can be set to all legal
+        // values.
+        let panningModels = ['equalpower', 'HRTF'];
+
+        for (let i = 0; i < panningModels.length; ++i) {
+          should(function() {
+            panner.panningModel = panningModels[i];
+          }, 'Set panner.panningModel = "' + panningModels[i] + '"').notThrow();
+
+          should(
+              panner.panningModel,
+              'panner.panningModel = "' + panningModels[i] + '"')
+              .beEqualTo(panningModels[i]);
+        }
+
+        should(function() {
+          panner.panningModel = 'invalid';
+        }, 'panner.panningModel = "invalid"').notThrow();
+
+        should(panner.panningModel, 'panner.panningModel after invalid setter')
+            .beEqualTo('HRTF');
+
+        // Check that numerical values are no longer supported.  We shouldn't
+        // throw and the value shouldn't be changed.
+        panner.panningModel = 'HRTF';
+        should(function() {
+          panner.panningModel = 1;
+        }, 'panner.panningModel = 1').notThrow();
+
+        should(panner.panningModel, 'panner.panningModel').beEqualTo('HRTF');
+
+        task.done();
+      });
+
+      audit.define('distance models', (task, should) => {
+        // Check that the .panningModel attribute can be set to all legal
+        // values.
+        let distanceModels = ['linear', 'inverse', 'exponential'];
+
+        for (let i = 0; i < distanceModels.length; ++i) {
+          should(function() {
+            panner.distanceModel = distanceModels[i];
+          }, 'panner.distanceModel = "' + distanceModels[i] + '"').notThrow();
+
+          should(
+              panner.distanceModel,
+              'panner.distanceModel = "' + distanceModels[i] + '"')
+              .beEqualTo(distanceModels[i]);
+        }
+
+        should(function() {
+          panner.distanceModel = 'invalid';
+        }, 'panner.distanceModel = "invalid"').notThrow();
+
+        should(panner.distanceModel, 'panner.distanceModel')
+            .beEqualTo('exponential');
+
+        task.done();
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webauthn/createcredential-badargs-attestation.https.html b/webauthn/createcredential-badargs-attestation.https.html
new file mode 100644
index 0000000..a56f4f0
--- /dev/null
+++ b/webauthn/createcredential-badargs-attestation.https.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() attestation parameter Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    // attestation bad values
+    new CreateCredentialsTest("options.publicKey.attestation", {}).runTest("Bad attestation parameter: attestation is empty object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.attestation", []).runTest("Bad attestation parameter: attestation is empty array", new TypeError());
+    new CreateCredentialsTest("options.publicKey.attestation", null).runTest("Bad attestation parameter: attestation is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.attestation", "noneofyourbusiness").runTest("Bad attestation parameter: attestation is \"noneofyourbusiness\"", new TypeError());
+    new CreateCredentialsTest("options.publicKey.attestation", "").runTest("Bad attestation parameter: attestation is empty string", new TypeError());
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-badargs-authnrselection.https.html b/webauthn/createcredential-badargs-authnrselection.https.html
new file mode 100644
index 0000000..2c42135
--- /dev/null
+++ b/webauthn/createcredential-badargs-authnrselection.https.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() authenticator selection Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var defaultAuthnrSel = {
+        authenticatorAttachment: "cross-platform",
+        requireResidentKey: false,
+        userVerification: "preferred"
+    };
+    // attachment
+    var authnrSelAttachPlatform = cloneObject(defaultAuthnrSel);
+    authnrSelAttachPlatform.authenticatorAttachment = "platform";
+    var authnrSelBadAttachEmptyStr = cloneObject(defaultAuthnrSel);
+    authnrSelBadAttachEmptyStr.authenticatorAttachment = "";
+    var authnrSelBadAttachEmptyObj = cloneObject(defaultAuthnrSel);
+    authnrSelBadAttachEmptyObj.authenticatorAttachment = {};
+    var authnrSelBadAttachNull = cloneObject(defaultAuthnrSel);
+    authnrSelBadAttachNull.authenticatorAttachment = null;
+    // resident key
+    var authnrSelRkTrue = cloneObject(defaultAuthnrSel);
+    authnrSelRkTrue.requireResidentKey = true;
+    var authnrSelRkBadString = cloneObject(defaultAuthnrSel);
+    authnrSelRkBadString.requireResidentKey = "foo";
+    // user verification
+    var authnrSelUvRequired = cloneObject(defaultAuthnrSel);
+    authnrSelUvRequired.userVerification = "required";
+    var authnrSelBadUvEmptyStr = cloneObject(defaultAuthnrSel);
+    authnrSelBadUvEmptyStr.userVerification = "";
+    var authnrSelBadUvEmptyObj = cloneObject(defaultAuthnrSel);
+    authnrSelBadUvEmptyObj.userVerification = {};
+    var authnrSelBadUvStr = cloneObject(defaultAuthnrSel);
+    authnrSelBadUvStr.userVerification = "requiredshirtshoestshirt";
+    var authnrSelBadUvNull = cloneObject(defaultAuthnrSel);
+    authnrSelBadUvNull.userVerification = null;
+
+    // authenticatorSelection bad values
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", []).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty array", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", null).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", "").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", "none").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is string", new TypeError());
+
+    // authenticatorSelection bad attachment values
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadAttachEmptyStr).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is empty string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadAttachEmptyObj).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is empty object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadAttachNull).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is null", new TypeError());
+    // XXX: assumes authnr is behaving like most U2F authnrs; really depends on the authnr or mock configuration
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelAttachPlatform).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment platform", "NotAllowedError");
+
+    // authenticatorSelection bad requireResidentKey values
+   // XXX: assumes authnr is behaving like most U2F authnrs; really depends on the authnr or mock configuration
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkTrue).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey true", "NotAllowedError");
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkBadString).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey is string", new TypeError());
+    // TODO: not sure if rk is "boolean" or "truthy"; add test cases if it should only accept boolean values
+
+    // authenticatorSelection bad userVerification values
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvEmptyStr).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvEmptyObj).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvStr).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification bad value", new TypeError());
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvNull).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification null", new TypeError());
+    // XXX: assumes this is a mock authenticator the properly reports that it is not doing userVerfication
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelUvRequired).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification required", "NotAllowedError");
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest, cloneObject */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-badargs-challenge.https.html b/webauthn/createcredential-badargs-challenge.https.html
new file mode 100644
index 0000000..0ad67f2
--- /dev/null
+++ b/webauthn/createcredential-badargs-challenge.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() challenge Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    // bad challenge values
+    new CreateCredentialsTest({path: "options.publicKey.challenge", value: undefined}).runTest("Bad challenge: challenge missing", new TypeError());
+    new CreateCredentialsTest("options.publicKey.challenge", "hi mom").runTest("Bad challenge: challenge is string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.challenge", null).runTest("Bad challenge: challenge is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.challenge", {}).runTest("Bad challenge: challenge is empty object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.challenge", new Array()).runTest("Bad challenge: challenge is empty Array", new TypeError());
+    new CreateCredentialsTest("options.publicKey.challenge", new ArrayBuffer(0)).runTest("Bad challenge: challenge is empty ArrayBuffer", new TypeError());
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-badargs-rp.https.html b/webauthn/createcredential-badargs-rp.https.html
index f06e02c..9958b2c 100644
--- a/webauthn/createcredential-badargs-rp.https.html
+++ b/webauthn/createcredential-badargs-rp.https.html
@@ -12,29 +12,29 @@
     "use strict";
 
     // rp bad values
-    new CreateCredentialsTest({path: "options.publicKey.rp", value: undefined}).testBadArgs("rp missing");
-    new CreateCredentialsTest("options.publicKey.rp", "hi mom").testBadArgs("rp is string");
-    // new CreateCredentialsTest("options.publicKey.rp", {}).testBadArgs("rp is empty object");
+    new CreateCredentialsTest({path: "options.publicKey.rp", value: undefined}).runTest("Bad rp: rp missing", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp", "hi mom").runTest("Bad rp: rp is string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp", {}).runTest("Bad rp: rp is empty object", new TypeError());
 
-    // rp.id
-    // new CreateCredentialsTest({path: "options.publicKey.rp.id", value: undefined}).testBadArgs("rp missing id");
-    new CreateCredentialsTest("options.publicKey.rp.id", {}).testBadArgs("Bad rp: id is object");
-    new CreateCredentialsTest("options.publicKey.rp.id", null).testBadArgs("Bad rp: id is null");
-    new CreateCredentialsTest("options.publicKey.rp.id", "").testBadArgs("Bad rp: id is empty String");
+    // // rp.id
+    new CreateCredentialsTest("options.publicKey.rp.id", {}).runTest("Bad rp: id is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.id", null).runTest("Bad rp: id is null", "SecurityError");
+    new CreateCredentialsTest("options.publicKey.rp.id", "").runTest("Bad rp: id is empty String", "SecurityError");
+    new CreateCredentialsTest("options.publicKey.rp.id", "invalid domain.com").runTest("Bad rp: id is invalid domain (has space)", "SecurityError");
+    new CreateCredentialsTest("options.publicKey.rp.id", "-invaliddomain.com").runTest("Bad rp: id is invalid domain (starts with dash)", "SecurityError");
+    new CreateCredentialsTest("options.publicKey.rp.id", "0invaliddomain.com").runTest("Bad rp: id is invalid domain (starts with number)", "SecurityError");
 
-    // rp.name
-    // new CreateCredentialsTest({path: "options.publicKey.rp.name", value: undefined}).testBadArgs("rp missing name");
-    new CreateCredentialsTest("options.publicKey.rp.name", {}).testBadArgs("Bad rp: name is object");
-    new CreateCredentialsTest("options.publicKey.rp.name", null).testBadArgs("Bad rp: name is null");
-    new CreateCredentialsTest("options.publicKey.rp.name", "").testBadArgs("Bad rp: name is empty String");
+    // // rp.name
+    new CreateCredentialsTest({path: "options.publicKey.rp.name", value: undefined}).runTest("rp missing name", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.name", {}).runTest("Bad rp: name is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.name", null).runTest("Bad rp: name is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.name", "").runTest("Bad rp: name is empty String", new TypeError());
 
-    // rp.icon
-    // new CreateCredentialsTest({path: "options.publicKey.rp.icon", value: undefined}).testBadArgs("rp missing icon");
-    new CreateCredentialsTest("options.publicKey.rp.icon", {}).testBadArgs("Bad rp: icon is object");
-    new CreateCredentialsTest("options.publicKey.rp.icon", null).testBadArgs("Bad rp: icon is null");
-    new CreateCredentialsTest("options.publicKey.rp.icon", "").testBadArgs("Bad rp: icon is empty String");
-    // TODO: see https://github.com/w3c/webauthn/issues/587 for the 'undefined' tests that are commented out above
-    // TODO: unicode tests for icon URL (see also: USVString)
+    // // rp.icon
+    new CreateCredentialsTest("options.publicKey.rp.icon", {}).runTest("Bad rp: icon is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.icon", null).runTest("Bad rp: icon is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.rp.icon", "").runTest("Bad rp: icon is empty String", new TypeError());
+    // // TODO: unicode tests for icon URL (see also: USVString)
 });
 
 /* JSHINT */
diff --git a/webauthn/createcredential-badargs-user.https.html b/webauthn/createcredential-badargs-user.https.html
new file mode 100644
index 0000000..dd1870a
--- /dev/null
+++ b/webauthn/createcredential-badargs-user.https.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() user Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    // user bad values
+    new CreateCredentialsTest({path: "options.publicKey.user", value: undefined}).runTest("Bad user: user missing", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user", "hi mom").runTest("Bad user: user is string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user", {}).runTest("Bad user: user is empty object", new TypeError());
+
+    // // user.id
+    new CreateCredentialsTest({path: "options.publicKey.user.id", value: undefined}).runTest("Bad user: id is undefined", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", {}).runTest("Bad user: id is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", null).runTest("Bad user: id is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", "").runTest("Bad user: id is empty String", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new Array()).runTest("Bad user: id is empty Array", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new ArrayBuffer(0)).runTest("Bad user: id is empty ArrayBuffer", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new ArrayBuffer(65)).runTest("Bad user: ArrayBuffer id is too long (65 bytes)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new Int16Array(33)).runTest("Bad user: Int16Array id is too long (66 bytes)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new Int32Array(17)).runTest("Bad user: Int32Array id is too long (68 bytes)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new Float32Array(17)).runTest("Bad user: Float32Array id is too long (68 bytes)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.id", new Float64Array(9)).runTest("Bad user: Float64Array id is too long (72 bytes)", new TypeError());
+    var buf = new ArrayBuffer(65);
+    new CreateCredentialsTest("options.publicKey.user.id", new DataView(buf)).runTest("Bad user: id is too long (65 bytes)", new TypeError());
+
+    // // user.name
+    new CreateCredentialsTest({path: "options.publicKey.user.name", value: undefined}).runTest("user missing name", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.name", {}).runTest("Bad user: name is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.name", null).runTest("Bad user: name is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.name", "").runTest("Bad user: name is empty String", new TypeError());
+
+    // // user.icon
+    new CreateCredentialsTest("options.publicKey.user.icon", {}).runTest("Bad user: icon is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.icon", null).runTest("Bad user: icon is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.icon", "").runTest("Bad user: icon is empty String", new TypeError());
+    // // TODO: unicode tests for icon URL (see also: USVString)
+
+    // // user.displayName
+    new CreateCredentialsTest({path: "options.publicKey.user.displayName", value: undefined}).runTest("Bad user: displayName is undefined", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.displayName", {}).runTest("Bad user: displayName is object", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.displayName", null).runTest("Bad user: displayName is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.user.displayName", "").runTest("Bad user: displayName is empty String", new TypeError());
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-excludecredentials.https.html b/webauthn/createcredential-excludecredentials.https.html
new file mode 100644
index 0000000..a4cfb0a
--- /dev/null
+++ b/webauthn/createcredential-excludecredentials.https.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() excludeCredentials Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    // bad excludeCredentials values
+    new CreateCredentialsTest("options.publicKey.excludeCredentials", "hi mom").runTest("Bad excludeCredentials: string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.excludeCredentials", {}).runTest("Bad excludeCredentials: empty object", new TypeError());
+    // TODO: bad excludeCredentials with [{.type}] or [{.id}] or [{.transports}] wrong
+
+    // good excludeCredentials values
+    new CreateCredentialsTest({path: "options.publicKey.excludeCredentials", value: undefined}).runTest("excludeCredentials missing");
+    new CreateCredentialsTest("options.publicKey.excludeCredentials", []).runTest("excludeCredentials empty array");
+
+    // proper excludeCredentials behavior
+    // should error on excluding existing credential
+    promise_test((t) => {
+        var cred1;
+        return Promise.resolve()
+            .then(() => {
+                return createCredential();
+            })
+            .then((cred) => {
+                cred1 = cred;
+                var excludeCred = {
+                    id: cred.rawId,
+                    type: "public-key"
+                };
+                var args = {
+                    options: {
+                        publicKey: {
+                            excludeCredentials: [excludeCred]
+                        }
+                    }
+                };
+                var p = createCredential(args);
+                return promise_rejects (t, "NotAllowedError", p, "expected to fail on excluded credenetial");
+            });
+    }, "exclude existing credential");
+
+    // should not error on excluding random credential
+    promise_test(() => {
+        return Promise.resolve()
+            .then(() => {
+                return createCredential();
+            })
+            .then(() => {
+                var randomCredId = new Uint8Array(162);
+                window.crypto.getRandomValues(randomCredId);
+
+                var excludeCred = {
+                    id: randomCredId,
+                    type: "public-key"
+                };
+                var args = {
+                    options: {
+                        publicKey: {
+                            excludeCredentials: [excludeCred]
+                        }
+                    }
+                };
+                return createCredential(args);
+            });
+    }, "exclude random (non-existing) credential");
+
+    // TODO: exclude including transport type (USB, BLE, NFC)
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest, createCredential, promise_test, promise_rejects */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-extensions.https.html b/webauthn/createcredential-extensions.https.html
new file mode 100644
index 0000000..3b3c9bf
--- /dev/null
+++ b/webauthn/createcredential-extensions.https.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() extensions Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var dummyExtension = {
+        foo: true,
+        bar: "yup"
+    };
+
+    // bad extension values
+    new CreateCredentialsTest("options.publicKey.extensions", "hi mom").runTest("Bad extensions: extensions is string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.extensions", null).runTest("Bad extensions: extensions is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.extensions", []).runTest("Bad extensions: extensions is empty Array", new TypeError());
+    new CreateCredentialsTest("options.publicKey.extensions", new ArrayBuffer(0)).runTest("Bad extensions: extensions is empty ArrayBuffer", new TypeError());
+    var badJson = '{"foo": true, "bar: "yup"}'; // missing quote after "bar"
+    new CreateCredentialsTest("options.publicKey.extensions", {foo: badJson}).runTest("Bad extensions: malformatted JSON", new TypeError());
+    new CreateCredentialsTest("options.publicKey.extensions", {foo: dummyExtension}).runTest("Bad extensions: JavaScript object", new TypeError());
+    var badExtId = {};
+    badExtId[createRandomString(65)] = dummyExtension;
+    new CreateCredentialsTest("options.publicKey.extensions", {badExtId: dummyExtension}).runTest("Bad extensions: extension ID too long", new TypeError());
+
+    // phony extensions
+    // TODO: not sure if this should pass or fail
+    // should be clarified as part of https://github.com/w3c/webauthn/pull/765
+    var randomExtId = {};
+    randomExtId[createRandomString(64)] = dummyExtension;
+    new CreateCredentialsTest("options.publicKey.extensions", {foo: JSON.stringify(randomExtId)}).runTest("extensions is a nonsensical JSON string");
+
+    // TODO
+    // defined extensions:
+    // * appid
+    // * txAuthSimple
+    // * txAuthGeneric
+    // * authnSel
+    // * exts
+    // * uvi
+    // * loc
+    // * uvm
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest, createRandomString */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-passing.https.html b/webauthn/createcredential-passing.https.html
index a66d3c1..25dba1d 100644
--- a/webauthn/createcredential-passing.https.html
+++ b/webauthn/createcredential-passing.https.html
@@ -12,9 +12,108 @@
     "use strict";
 
     // CreateCredentialTest passing tests
-    new CreateCredentialsTest().test();
+
+    // default arguments
+    new CreateCredentialsTest().runTest("passing credentials.create() with default arguments");
+
+    // rp
+    new CreateCredentialsTest({path: "options.publicKey.rp.id", value: window.location.host}).runTest("passing credentials.create() with rpId (host and port)");
+    new CreateCredentialsTest({path: "options.publicKey.rp.id", value: window.location.hostname}).runTest("passing credentials.create() with rpId (hostname)");
+    new CreateCredentialsTest({path: "options.publicKey.rp.icon", value: undefined}).runTest("passing credentials.create() without rp.icon");
+
+    // user
+    new CreateCredentialsTest("options.publicKey.user.id", new ArrayBuffer(1)).runTest("very short user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new ArrayBuffer(64)).runTest("max length user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new Uint8Array(64)).runTest("Uint8Array user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new Int8Array(64)).runTest("Int8Array user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new Int16Array(32)).runTest("Int16Array user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new Int32Array(16)).runTest("Int32Array user id");
+    new CreateCredentialsTest("options.publicKey.user.id", new Float32Array(16)).runTest("Float32Array user id");
+    var dvBuf1 = new ArrayBuffer(16);
+    new CreateCredentialsTest("options.publicKey.user.id", new DataView(dvBuf1)).runTest("DataView user id");
+    new CreateCredentialsTest({path: "options.publicKey.user.icon", value: undefined}).runTest("passing credentials.create() without user.icon");
+
+    // good challenge values
+    // all these challenges are zero-filled buffers... think anyone will complain?
+    new CreateCredentialsTest("options.publicKey.challenge", new Int16Array(33)).runTest("Int16Array challenge");
+    new CreateCredentialsTest("options.publicKey.challenge", new Int32Array(17)).runTest("Int32Array challenge");
+    new CreateCredentialsTest("options.publicKey.challenge", new Float32Array(17)).runTest("Float32Array challenge");
+    new CreateCredentialsTest("options.publicKey.challenge", new Float64Array(9)).runTest("Float64Array challenge");
+    var dvBuf2 = new ArrayBuffer(65);
+    new CreateCredentialsTest("options.publicKey.challenge", new DataView(dvBuf2)).runTest("DataView challenge");
+    new CreateCredentialsTest("options.publicKey.challenge", new ArrayBuffer(8192)).runTest("Absurdly large challenge");
+
+    // good pubKeyCredParams values
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", []).runTest("Bad pubKeyCredParams: pubKeyCredParams is empty Array");
+    const pkParamEC256 = {
+        type: "public-key",
+        alg: cose_alg_ECDSA_w_SHA256
+    };
+    const pkParamEC512 = {
+        type: "public-key",
+        alg: cose_alg_ECDSA_w_SHA512
+    };
+    // XXX: presumes all mock authenticators support EC256
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [pkParamEC256]).runTest("EC256 pubKeyCredParams");
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [pkParamEC512, pkParamEC256])
+        .runTest("SelectEC256 pubKeyCredParams from a list");
+    // TODO: currently most browsers are mocking FIDO U2F, which is EC256 only
+    // new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [pkParamEC512]).runTest("EC512 pubKeyCredParams");
+
+    // NOTE: excludeCredentials parameter -- see also: createcredential-excludecredentials.https.html
+
+    // timeout
+    new CreateCredentialsTest({path: "options.publicKey.timeout", value: undefined}).runTest("passing credentials.create() with no timeout");
+
+    // valid authenticatorSelection values
+    var defaultAuthnrSel = {
+        authenticatorAttachment: "cross-platform",
+        requireResidentKey: false,
+        userVerification: "preferred"
+    };
+    // attachment
+    var authnrSelAttachUndef = cloneObject(defaultAuthnrSel);
+    authnrSelAttachUndef.authenticatorAttachment = undefined;
+    // resident key
+    var authnrSelRkUndef = cloneObject(defaultAuthnrSel);
+    authnrSelRkUndef.requireResidentKey = undefined;
+    var authnrSelRkFalse = cloneObject(defaultAuthnrSel);
+    authnrSelRkFalse.requireResidentKey = false;
+    // user verification
+    var authnrSelUvUndef = cloneObject(defaultAuthnrSel);
+    authnrSelUvUndef.userVerification = undefined;
+    var authnrSelUvDiscouraged = cloneObject(defaultAuthnrSel);
+    authnrSelUvDiscouraged.userVerification = "discouraged";
+    new CreateCredentialsTest({path: "options.publicKey.authenticatorSelection", value: undefined}).runTest("authenticatorSelection is undefined");
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", {}).runTest("authenticatorSelection is empty object");
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", cloneObject(defaultAuthnrSel)).runTest("authenticatorSelection default values");
+
+    // authnr selection attachment
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelAttachUndef).runTest("authenticatorSelection attachment undefined");
+
+    // authnr selection resident key
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkUndef).runTest("authenticatorSelection residentKey undefined");
+    // XXX: assumes authnr is behaving like most U2F authnrs; really depends on the authnr or mock configuration
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkFalse).runTest("authenticatorSelection residentKey false");
+
+    // authnr selection user verification
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelUvUndef).runTest("authenticatorSelection userVerification undefined");
+    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelUvDiscouraged).runTest("authenticatorSelection userVerification discouraged");
+
+
+    // good attestation values
+    new CreateCredentialsTest("options.publicKey.attestation", "none").runTest("attestation parameter: attestation is \"none\"");
+    new CreateCredentialsTest("options.publicKey.attestation", "indirect").runTest("attestation parameter: attestation is \"indirect\"");
+    new CreateCredentialsTest("options.publicKey.attestation", "direct").runTest("attestation parameter: attestation is \"direct\"");
+    new CreateCredentialsTest({path: "options.publicKey.attestation", value: undefined}).runTest("attestation parameter: attestation is undefined");
+    // TODO: test this with multiple mock authenticators to make sure that the right options are chosen when available?
+
+    // good extension values
+    new CreateCredentialsTest({path: "options.publicKey.extensions", value: undefined}).runTest("extensions undefined");
+    new CreateCredentialsTest("options.publicKey.extensions", {}).runTest("extensions are empty object");
+    new CreateCredentialsTest("options.publicKey.extensions", {foo: "", bar: "", bat: ""}).runTest("extensions are dict of empty strings");
 });
 
 /* JSHINT */
-/* globals standardSetup, CreateCredentialsTest */
+/* globals standardSetup, CreateCredentialsTest, cose_alg_ECDSA_w_SHA256, cose_alg_ECDSA_w_SHA512, cloneObject */
 </script>
\ No newline at end of file
diff --git a/webauthn/createcredential-pubkeycredparams.https.html b/webauthn/createcredential-pubkeycredparams.https.html
new file mode 100644
index 0000000..325191c
--- /dev/null
+++ b/webauthn/createcredential-pubkeycredparams.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() pubKeyCredParams Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var badType = {
+        type: "something-else",
+        alg: cose_alg_ECDSA_w_SHA512
+    };
+    var badTypeEmptyString = cloneObject(badType);
+    badTypeEmptyString.type = "";
+    var badTypeNull = cloneObject(badType);
+    badTypeNull.type = null;
+    var badTypeEmptyObj = cloneObject(badType);
+    badTypeEmptyObj.type = {};
+
+    var badAlg = {
+        type: "public-key",
+        alg: 42
+    };
+    var badAlgZero = cloneObject(badAlg);
+    badAlgZero.alg = 0;
+
+    // bad pubKeyCredParams values
+    new CreateCredentialsTest({path: "options.publicKey.pubKeyCredParams", value: undefined}).runTest("Bad pubKeyCredParams: pubKeyCredParams is undefined", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", "hi mom").runTest("Bad pubKeyCredParams: pubKeyCredParams is string", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", null).runTest("Bad pubKeyCredParams: pubKeyCredParams is null", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badType]).runTest("Bad pubKeyCredParams: first param has bad type (\"something-else\")", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badTypeEmptyString]).runTest("Bad pubKeyCredParams: first param has bad type (\"\")", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badTypeNull]).runTest("Bad pubKeyCredParams: first param has bad type (null)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badTypeEmptyObj]).runTest("Bad pubKeyCredParams: first param has bad type (empty object)", new TypeError());
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badAlg]).runTest("Bad pubKeyCredParams: first param has bad alg (42)", "NotSupportedError");
+    new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badAlgZero]).runTest("Bad pubKeyCredParams: first param has bad alg (0)", "NotSupportedError");
+
+    // TODO: come back to this when mock authenticators support multiple cryptos so that we can test the preference ranking
+    // function verifyEC256(res) {
+    //     debug ("verifyEC256 got", res);
+    //     debug ("client data JSON", ab2str(res.response.clientDataJSON));
+    //     parseAuthenticatorData(res.response.attestationObject);
+    // }
+    // new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [pkParamEC256, pkParamEC512])
+    //     .afterTest(verifyEC256)
+    //     .runTest("EC256, EC512 pubKeyCredParams");
+    // function verifyEC512(res) {
+    //     debug ("verifyEC512 got", res);
+    //     debug ("client data JSON", ab2str(res.response.clientDataJSON));
+    //     // parseAuthenticatorData(res.response.attestationObject);
+    //     printHex ("clientDataJSON", res.response.clientDataJSON);
+    //     printHex ("attestationObject", res.response.attestationObject);
+    // }
+    // new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [pkParamEC512, pkParamEC256])
+    //     .afterTest(verifyEC512)
+    //     .runTest("EC512, EC256 pubKeyCredParams");
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest, cose_alg_ECDSA_w_SHA512, cloneObject */
+</script>
\ No newline at end of file
diff --git a/webauthn/createcredential-timeout.https.html b/webauthn/createcredential-timeout.https.html
new file mode 100644
index 0000000..b94ae58
--- /dev/null
+++ b/webauthn/createcredential-timeout.https.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.create() timeout Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    // bad timeout values
+    // TODO: there is some debate as to whether MAX_UNSIGNED_LONG + 1 and / or -1 should be disallowed since they get converted to valid values internally
+    // new CreateCredentialsTest({path: "options.publicKey.timeout", value: -1}).runTest("Bad timeout: negative", new TypeError());
+    // new CreateCredentialsTest({path: "options.publicKey.timeout", value: 4294967295 + 1}).runTest("Bad timeout: too big", new TypeError());
+
+    // timeout test
+    // XXX: this probably always passes with most mock authenticators unless
+    // some setup happens right here to make sure they don't return a credential
+    // right away. So... uhh... I guess test this with a real authenticator if you
+    // want to see if it really works.
+    promise_test(() => {
+        return new Promise((resolve, reject) => {
+            var args = {
+                options: {
+                    publicKey: {
+                        timeout: 1
+                    }
+                }
+            };
+
+            setTimeout(() => {
+                reject(new Error ("timed out"));
+            }, 1000);
+
+            createCredential(args).then((res) => {
+                resolve(res);
+            });
+        });
+    }, "ensure create credential times out");
+    // TODO: createCredential.timeout > 1s && setTimeout < 1s
+    // TODO: createCredential.timeout < 5s && setTimeout > 5s
+});
+
+/* JSHINT */
+/* globals standardSetup, CreateCredentialsTest, createCredential, promise_test */
+</script>
\ No newline at end of file
diff --git a/webauthn/getcredential-badargs-rpid.https.html b/webauthn/getcredential-badargs-rpid.https.html
new file mode 100644
index 0000000..9e8da4d
--- /dev/null
+++ b/webauthn/getcredential-badargs-rpid.https.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn credential.get() rpId Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var credPromise = createCredential();
+
+    new GetCredentialsTest("options.publicKey.rpId", "")
+        .addCredential(credPromise)
+        .runTest("Bad rpId: empty string", "SecurityError");
+    new GetCredentialsTest("options.publicKey.rpId", null)
+        .addCredential(credPromise)
+        .runTest("Bad rpId: null", "SecurityError");
+    new GetCredentialsTest("options.publicKey.rpId", "invalid domain.com")
+        .addCredential(credPromise)
+        .runTest("Bad rpId: invalid domain (has space)", "SecurityError");
+    new GetCredentialsTest("options.publicKey.rpId", "-invaliddomain.com")
+        .addCredential(credPromise)
+        .runTest("Bad rpId: invalid domain (starts with dash)", "SecurityError");
+    new GetCredentialsTest("options.publicKey.rpId", "0invaliddomain.com")
+        .addCredential(credPromise)
+        .runTest("Bad rpId: invalid domain (starts with number)", "SecurityError");
+});
+
+/* JSHINT */
+/* globals standardSetup, GetCredentialsTest, createCredential */
+</script>
\ No newline at end of file
diff --git a/webauthn/getcredential-badargs-userverification.https.html b/webauthn/getcredential-badargs-userverification.https.html
new file mode 100644
index 0000000..6101540
--- /dev/null
+++ b/webauthn/getcredential-badargs-userverification.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.get() user verification Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var credPromise = createCredential();
+
+    // authenticatorSelection bad userVerification values
+    new GetCredentialsTest("options.publicKey.userVerification", "")
+        .addCredential(credPromise)
+        .runTest("Bad userVerification: empty string", new TypeError());
+    new GetCredentialsTest("options.publicKey.userVerification", {})
+        .addCredential(credPromise)
+        .runTest("Bad userVerification: empty object", new TypeError());
+    new GetCredentialsTest("options.publicKey.userVerification", "requiredshirtshoestshirt")
+        .addCredential(credPromise)
+        .runTest("Bad userVerification: bad value", new TypeError());
+    new GetCredentialsTest("options.publicKey.userVerification", null)
+        .addCredential(credPromise)
+        .runTest("Bad userVerification: null", new TypeError());
+    // XXX: assumes this is a mock authenticator the properly reports that it is not doing userVerfication
+    new GetCredentialsTest("options.publicKey.userVerification", "required")
+        .addCredential(credPromise)
+        .runTest("Bad userVerification: \"required\"", "NotAllowedError");
+});
+
+/* JSHINT */
+/* globals standardSetup, GetCredentialsTest, createCredential */
+</script>
\ No newline at end of file
diff --git a/webauthn/getcredential-extensions.https.html b/webauthn/getcredential-extensions.https.html
new file mode 100644
index 0000000..bad5ce4
--- /dev/null
+++ b/webauthn/getcredential-extensions.https.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.get() extensions Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var dummyExtension = {
+        foo: true,
+        bar: "yup"
+    };
+    var credPromise = createCredential();
+
+    // bad extension values
+    new GetCredentialsTest("options.publicKey.extensions", "hi mom")
+        .addCredential(credPromise)
+        .runTest("Bad extensions: extensions is string", new TypeError());
+    new GetCredentialsTest("options.publicKey.extensions", null)
+        .addCredential(credPromise)
+        .runTest("Bad extensions: extensions is null", new TypeError());
+    new GetCredentialsTest("options.publicKey.extensions", [])
+        .addCredential(credPromise)
+        .runTest("Bad extensions: extensions is empty Array", new TypeError());
+    new GetCredentialsTest("options.publicKey.extensions", new ArrayBuffer(0))
+        .addCredential(credPromise)
+        .runTest("Bad extensions: extensions is empty ArrayBuffer", new TypeError());
+    var badJson = '{"foo": true, "bar: "yup"}'; // missing quote after "bar"
+    new GetCredentialsTest("options.publicKey.extensions", {foo: badJson})
+        .addCredential(credPromise)
+        .runTest("Bad extensions: malformatted JSON", new TypeError());
+    new GetCredentialsTest("options.publicKey.extensions", {foo: dummyExtension})
+        .addCredential(credPromise)
+        .runTest("Bad extensions: JavaScript object", new TypeError());
+    var badExtId = {};
+    badExtId[createRandomString(65)] = dummyExtension;
+    new GetCredentialsTest("options.publicKey.extensions", {badExtId: dummyExtension})
+        .addCredential(credPromise)
+        .runTest("Bad extensions: extension ID too long", new TypeError());
+
+    // phony extensions
+    // TODO: not sure if this should pass or fail
+    // should be clarified as part of https://github.com/w3c/webauthn/pull/765
+    var randomExtId = {};
+    randomExtId[createRandomString(64)] = dummyExtension;
+    new GetCredentialsTest("options.publicKey.extensions", {foo: JSON.stringify(randomExtId)})
+        .addCredential(credPromise)
+        .runTest("extensions is a nonsensical JSON string");
+
+    // TODO
+    // defined extensions:
+    // * appid
+    // * txAuthSimple
+    // * txAuthGeneric
+    // * authnSel
+    // * exts
+    // * uvi
+    // * loc
+    // * uvm
+});
+
+/* JSHINT */
+/* globals standardSetup, GetCredentialsTest, createRandomString, createCredential */
+</script>
\ No newline at end of file
diff --git a/webauthn/getcredential-passing.https.html b/webauthn/getcredential-passing.https.html
index c7cf794..58b085f 100644
--- a/webauthn/getcredential-passing.https.html
+++ b/webauthn/getcredential-passing.https.html
@@ -11,11 +11,56 @@
 standardSetup(function() {
     "use strict";
 
-    // GetCredentialsTest passing tests
-    // new GetCredentialsTest().addCredential();
-    new GetCredentialsTest().addCredential().test();
+    var credPromise = createCredential();
+
+    // GetCredentialsTest with default args
+    new GetCredentialsTest()
+        .addCredential(credPromise)
+        .runTest("passing credentials.get() with default args");
+
+    // timeout
+    new GetCredentialsTest({path: "options.publicKey.timeout", value: undefined})
+        .addCredential(credPromise)
+        .runTest("passing credentials.create() with no timeout");
+
+    // rpId
+    new GetCredentialsTest({path: "options.publicKey.rpId", value: undefined})
+        .addCredential(credPromise)
+        .runTest("rpId undefined");
+    new GetCredentialsTest({path: "options.publicKey.rpId", value: window.location.host})
+        .addCredential(credPromise)
+        .runTest("passing credentials.get() with rpId (host and port)");
+    new GetCredentialsTest({path: "options.publicKey.rpId", value: window.location.hostname})
+        .addCredential(credPromise)
+        .runTest("passing credentials.get() with rpId (hostname)");
+
+    // allowCredentials
+    new GetCredentialsTest({path: "options.publicKey.allowCredentials", value: undefined})
+        .runTest("no credential specified");
+
+    // authnr selection user verification
+    new GetCredentialsTest({path: "options.publicKey.userVerification", value: undefined})
+        .addCredential(credPromise)
+        .runTest("authenticatorSelection userVerification undefined");
+    new GetCredentialsTest("options.publicKey.userVerification", "preferred")
+        .addCredential(credPromise)
+        .runTest("authenticatorSelection userVerification preferred");
+    new GetCredentialsTest("options.publicKey.userVerification", "discouraged")
+        .addCredential(credPromise)
+        .runTest("authenticatorSelection userVerification discouraged");
+
+    // good extension values
+    new GetCredentialsTest({path: "options.publicKey.extensions", value: undefined})
+        .addCredential(credPromise)
+        .runTest("extensions undefined");
+    new GetCredentialsTest("options.publicKey.extensions", {})
+        .addCredential(credPromise)
+        .runTest("extensions are empty object");
+    new GetCredentialsTest("options.publicKey.extensions", {foo: "", bar: "", bat: ""})
+        .addCredential(credPromise)
+        .runTest("extensions are dict of empty strings");
 });
 
 /* JSHINT */
-/* globals standardSetup, GetCredentialsTest */
+/* globals standardSetup, GetCredentialsTest, createCredential */
 </script>
\ No newline at end of file
diff --git a/webauthn/getcredential-timeout.https.html b/webauthn/getcredential-timeout.https.html
new file mode 100644
index 0000000..8f5c2f3
--- /dev/null
+++ b/webauthn/getcredential-timeout.https.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAuthn navigator.credentials.get() timeout Tests</title>
+<link rel="author" title="Adam Powers" href="mailto:adam@fidoalliance.org">
+<link rel="help" href="https://w3c.github.io/webauthn/#iface-credential">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=helpers.js></script>
+<body></body>
+<script>
+standardSetup(function() {
+    "use strict";
+
+    var credPromise = createCredential();
+
+    // bad timeout values
+    // TODO: there is some debate as to whether MAX_UNSIGNED_LONG + 1 and / or -1 should be disallowed since they get converted to valid values internally
+    // new GetCredentialsTest({path: "options.publicKey.timeout", value: -1})
+    //     .addCredential(credPromise)
+    //     .runTest("Bad timeout: negative", new TypeError());
+    // new GetCredentialsTest({path: "options.publicKey.timeout", value: 4294967295 + 1})
+    //     .addCredential(credPromise)
+    //     .runTest("Bad timeout: too big", new TypeError());
+
+    // timeout test
+    // XXX: this probably always passes with most mock authenticators unless
+    // some setup happens right here to make sure they don't return a credential
+    // right away. So... uhh... I guess test this with a real authenticator if you
+    // want to see if it really works.
+    var timer;
+    function startTimer() {
+        timer = setTimeout(() => {
+            throw new Error("Timer went off before timeout");
+        }, 1000);
+    }
+    function stopTimer() {
+        clearTimeout(timer);
+    }
+    new GetCredentialsTest({path: "options.publicKey.timeout", value: 1})
+        .addCredential(credPromise)
+        .beforeTest(startTimer)
+        .afterTest(stopTimer)
+        .runTest("ensure create credential times out");
+    // TODO: createCredential.timeout > 1s && setTimeout < 1s
+    // TODO: createCredential.timeout < 5s && setTimeout > 5s
+});
+
+/* JSHINT */
+/* globals standardSetup, GetCredentialsTest, createCredential */
+</script>
\ No newline at end of file
diff --git a/webauthn/helpers.js b/webauthn/helpers.js
index 11d0aee..568d3e2 100644
--- a/webauthn/helpers.js
+++ b/webauthn/helpers.js
@@ -1,5 +1,4 @@
-
-/* Useful constants for working with COSE key objects */
+// Useful constants for working with COSE key objects
 const cose_kty = 1;
 const cose_kty_ec2 = 2;
 const cose_alg = 3;
@@ -11,6 +10,117 @@
 const cose_crv_y = -3;
 
 /**
+ * These are the default arguments that will be passed to navigator.credentials.create()
+ * unless modified by a specific test case
+ */
+var createCredentialDefaultArgs = {
+    options: {
+        publicKey: {
+            // Relying Party:
+            rp: {
+                name: "Acme",
+                icon: "https://www.w3.org/StyleSheets/TR/2016/logos/W3C"
+            },
+
+            // User:
+            user: {
+                id: new Uint8Array(16), // Won't survive the copy, must be rebuilt
+                name: "john.p.smith@example.com",
+                displayName: "John P. Smith",
+                icon: "https://pics.acme.com/00/p/aBjjjpqPb.png"
+            },
+
+            pubKeyCredParams: [{
+                type: "public-key",
+                alg: cose_alg_ECDSA_w_SHA256,
+            }],
+
+            timeout: 60000, // 1 minute
+            excludeCredentials: [] // No excludeList
+        }
+    }
+};
+
+/**
+ * These are the default arguments that will be passed to navigator.credentials.get()
+ * unless modified by a specific test case
+ */
+var getCredentialDefaultArgs = {
+    options: {
+        publicKey: {
+            timeout: 60000
+            // allowCredentials: [newCredential]
+        }
+    }
+};
+
+function createCredential(opts) {
+    opts = opts || {};
+
+    // set the default options
+    var createArgs = cloneObject(createCredentialDefaultArgs);
+    let challengeBytes = new Uint8Array(16);
+    window.crypto.getRandomValues(challengeBytes);
+    createArgs.options.publicKey.challenge = challengeBytes;
+    createArgs.options.publicKey.user.id = new Uint8Array(16);
+
+    // change the defaults with any options that were passed in
+    extendObject (createArgs, opts);
+
+    // create the credential, return the Promise
+    return navigator.credentials.create(createArgs.options);
+}
+
+function createRandomString(len) {
+    var text = "";
+    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+    for(var i = 0; i < len; i++) {
+        text += possible.charAt(Math.floor(Math.random() * possible.length));
+    }
+    return text;
+}
+
+
+function ab2str(buf) {
+    return String.fromCharCode.apply(null, new Uint8Array(buf));
+}
+
+// Useful constants for working with attestation data
+const authenticator_data_user_present = 0x01;
+const authenticator_data_user_verified = 0x04;
+const authenticator_data_attested_cred_data = 0x40;
+const authenticator_data_extension_data = 0x80;
+
+function parseAuthenticatorData(buf) {
+    if (buf.byteLength < 37) {
+        throw new TypeError ("parseAuthenticatorData: buffer must be at least 37 bytes");
+    }
+
+    printHex ("authnrData", buf);
+
+    var authnrData = new DataView(buf);
+    var authnrDataObj = {};
+    authnrDataObj.length = buf.byteLength;
+
+    authnrDataObj.rpIdHash = new Uint8Array (buf.slice (0,32));
+    authnrDataObj.rawFlags = authnrData.getUint8(32);
+    authnrDataObj.counter = authnrData.getUint32(33, false);
+    authnrDataObj.rawCounter = [];
+    authnrDataObj.rawCounter[0] = authnrData.getUint8(33);
+    authnrDataObj.rawCounter[1] = authnrData.getUint8(34);
+    authnrDataObj.rawCounter[2] = authnrData.getUint8(35);
+    authnrDataObj.rawCounter[3] = authnrData.getUint8(36);
+    authnrDataObj.flags = {};
+
+    authnrDataObj.flags.userPresent = (authnrDataObj.rawFlags&authenticator_data_user_present)?true:false;
+    authnrDataObj.flags.userVerified = (authnrDataObj.rawFlags&authenticator_data_user_verified)?true:false;
+    authnrDataObj.flags.attestedCredentialData = (authnrDataObj.rawFlags&authenticator_data_attested_cred_data)?true:false;
+    authnrDataObj.flags.extensionData = (authnrDataObj.rawFlags&authenticator_data_extension_data)?true:false;
+
+    return authnrDataObj;
+}
+
+/**
  * TestCase
  *
  * A generic template for test cases
@@ -118,66 +228,127 @@
 
     /**
      * run the test function with the top-level properties of the test object applied as arguments
+     * expects the test to pass, and then validates the results
      */
-    test(desc) {
-        promise_test(() => {
-            return this.doIt()
-                .then((ret) => {
-                    // check the result
-                    this.validateRet(ret);
-                    return ret;
-                });
-        }, desc);
+    testPasses(desc) {
+        return this.doIt()
+            .then((ret) => {
+                // check the result
+                this.validateRet(ret);
+                return ret;
+            });
+    }
+
+    /**
+     * run the test function with the top-level properties of the test object applied as arguments
+     * expects the test to fail
+     */
+    testFails(t, testDesc, expectedErr) {
+        return promise_rejects(t, expectedErr, this.doIt(), "Expected bad parameters to fail");
+    }
+
+    /**
+     * Runs the test that's implemented by the class by calling the doIt() function
+     * @param  {String} desc                A description of the test being run
+     * @param  [Error|String] expectedErr   A string matching an error type, such as "SecurityError" or an object with a .name value that is an error type string
+     */
+    runTest(desc, expectedErr) {
+        promise_test((t) => {
+            return Promise.resolve().then(() => {
+                return this.testSetup();
+            }).then(() => {
+                if (expectedErr === undefined) {
+                    return this.testPasses(desc);
+                } else {
+                    return this.testFails(t, desc, expectedErr);
+                }
+            }).then((res) => {
+                return this.testTeardown(res);
+            })
+        }, desc)
+    }
+
+    /**
+     * called before runTest
+     * virtual method expected to be overridden by child class if needed
+     */
+    testSetup() {
+        if (this.beforeTestFn) {
+            this.beforeTestFn.call(this);
+        }
+
+        return Promise.resolve();
+    }
+
+    /**
+     * Adds a callback function that gets called in the TestCase context
+     * and within the testing process.
+     */
+    beforeTest(fn) {
+        if (typeof fn !== "function") {
+            throw new Error ("Tried to call non-function before test");
+        }
+
+        this.beforeTestFn = fn;
+
+        return this;
+    }
+
+    /**
+     * called after runTest
+     * virtual method expected to be overridden by child class if needed
+     */
+    testTeardown(res) {
+        if (this.afterTestFn) {
+            this.afterTestFn.call(this, res);
+        }
+
+        return Promise.resolve();
+    }
+
+    /**
+     * Adds a callback function that gets called in the TestCase context
+     * and within the testing process. Good for validating results.
+     */
+    afterTest(fn) {
+        if (typeof fn !== "function") {
+            throw new Error ("Tried to call non-function after test");
+        }
+
+        this.afterTestFn = fn;
+
+        return this;
     }
 
     /**
      * validates the value returned from the test function
+     * virtual method expected to be overridden by child class
      */
     validateRet() {
         throw new Error("Not implemented");
     }
-
-    /**
-     * calls doIt() with testObject() and expects it to fail with a TypeError()
-     */
-    testBadArgs(testDesc) {
-        promise_test(function(t) {
-            return promise_rejects(t, new TypeError(), this.doIt(), "Expected bad parameters to fail");
-        }.bind(this), testDesc);
-    }
 }
 
-var createCredentialDefaultArgs = {
-    options: {
-        publicKey: {
-            // Relying Party:
-            rp: {
-                name: "Acme"
-            },
-
-            // User:
-            user: {
-                id: new Uint8Array(), // Won't survive the copy, must be rebuilt
-                name: "john.p.smith@example.com",
-                displayName: "John P. Smith",
-                icon: "https://pics.acme.com/00/p/aBjjjpqPb.png"
-            },
-
-            pubKeyCredParams: [{
-                type: "public-key",
-                alg: cose_alg_ECDSA_w_SHA256,
-            }],
-
-            timeout: 60000, // 1 minute
-            excludeCredentials: [] // No excludeList
-        }
-    }
-};
-
 function cloneObject(o) {
     return JSON.parse(JSON.stringify(o));
 }
 
+function extendObject(dst, src) {
+    Object.keys(src).forEach(function(key) {
+        if (isSimpleObject(src[key])) {
+            extendObject (dst[key], src[key]);
+        } else {
+            dst[key] = src[key];
+        }
+    });
+}
+
+function isSimpleObject(o) {
+    return (typeof o === "object" &&
+        !Array.isArray(o) &&
+        !(o instanceof ArrayBuffer));
+}
+
 /**
  * CreateCredentialTest
  *
@@ -198,7 +369,7 @@
         window.crypto.getRandomValues(challengeBytes);
         this.testObject = cloneObject(createCredentialDefaultArgs);
         // cloneObject can't clone the BufferSource in user.id, so let's recreate it.
-        this.testObject.options.publicKey.user.id = new Uint8Array();
+        this.testObject.options.publicKey.user.id = new Uint8Array(16);
         this.testObject.options.publicKey.challenge = challengeBytes;
 
         // how to order the properties of testObject when passing them to makeCredential
@@ -235,15 +406,8 @@
         // default arguments
         let challengeBytes = new Uint8Array(16);
         window.crypto.getRandomValues(challengeBytes);
-        this.testObject = {
-            options: {
-                publicKey: {
-                    challenge: challengeBytes,
-                    // timeout: 60000,
-                    // allowCredentials: [newCredential]
-                }
-            }
-        };
+        this.testObject = cloneObject(getCredentialDefaultArgs);
+        this.testObject.options.publicKey.challenge = challengeBytes;
 
         // how to order the properties of testObject when passing them to makeCredential
         this.argOrder = [
@@ -266,33 +430,28 @@
         // if a Promise was passed in, add it to the list
         if (arg instanceof Promise) {
             this.credentialPromiseList.push(arg);
-            return;
+            return this;
         }
 
         // if a credential object was passed in, convert it to a Promise for consistency
         if (typeof arg === "object") {
             this.credentialPromiseList.push(Promise.resolve(arg));
-            return;
+            return this;
         }
 
-        // if a credential wasn't passed in, create one
-        let challengeBytes = new Uint8Array(16);
-        window.crypto.getRandomValues(challengeBytes);
-        var createArgs = cloneObject(createCredentialDefaultArgs);
-        createArgs.options.publicKey.challenge = challengeBytes;
-        createArgs.options.publicKey.user.id = new Uint8Array();
-        var p = navigator.credentials.create(createArgs.options);
+        // if no credential specified then create one
+        var p = createCredential();
         this.credentialPromiseList.push(p);
 
         return this;
     }
 
-    test() {
+    testSetup(desc) {
         if (!this.credentialPromiseList.length) {
             throw new Error("Attempting list without defining credential to test");
         }
 
-        Promise.all(this.credentialPromiseList)
+        return Promise.all(this.credentialPromiseList)
             .then((credList) => {
                 var idList = credList.map((cred) => {
                     return {
@@ -302,12 +461,15 @@
                     };
                 });
                 this.testObject.options.publicKey.allowCredentials = idList;
-                return super.test();
+                // return super.test(desc);
+            })
+            .catch((err) => {
+                throw Error(err);
             });
     }
 
     validateRet(ret) {
-        validatePublicKeyCredential (ret);
+        validatePublicKeyCredential(ret);
         validateAuthenticatorAssertionResponse(ret.response);
     }
 }
@@ -335,12 +497,16 @@
 function validateAuthenticatorAttestationResponse(attr) {
     // class
     assert_class_string(attr, "AuthenticatorAttestationResponse", "Expected credentials.create() to return instance of 'AuthenticatorAttestationResponse' class");
+
     // clientDataJSON
     assert_idl_attribute(attr, "clientDataJSON", "credentials.create() should return AuthenticatorAttestationResponse with clientDataJSON attribute");
     assert_readonly(attr, "clientDataJSON", "credentials.create() should return AuthenticatorAttestationResponse with readonly clientDataJSON attribute");
+    // TODO: clientDataJSON() and make sure fields are correct
+
     // attestationObject
     assert_idl_attribute(attr, "attestationObject", "credentials.create() should return AuthenticatorAttestationResponse with attestationObject attribute");
     assert_readonly(attr, "attestationObject", "credentials.create() should return AuthenticatorAttestationResponse with readonly attestationObject attribute");
+    // TODO: parseAuthenticatorData() and make sure flags are correct
 }
 
 /**
@@ -349,15 +515,20 @@
 function validateAuthenticatorAssertionResponse(assert) {
     // class
     assert_class_string(assert, "AuthenticatorAssertionResponse", "Expected credentials.create() to return instance of 'AuthenticatorAssertionResponse' class");
+
     // clientDataJSON
     assert_idl_attribute(assert, "clientDataJSON", "credentials.get() should return AuthenticatorAssertionResponse with clientDataJSON attribute");
     assert_readonly(assert, "clientDataJSON", "credentials.get() should return AuthenticatorAssertionResponse with readonly clientDataJSON attribute");
+    // TODO: clientDataJSON() and make sure fields are correct
+
     // signature
     assert_idl_attribute(assert, "signature", "credentials.get() should return AuthenticatorAssertionResponse with signature attribute");
     assert_readonly(assert, "signature", "credentials.get() should return AuthenticatorAssertionResponse with readonly signature attribute");
+
     // authenticatorData
     assert_idl_attribute(assert, "authenticatorData", "credentials.get() should return AuthenticatorAssertionResponse with authenticatorData attribute");
     assert_readonly(assert, "authenticatorData", "credentials.get() should return AuthenticatorAssertionResponse with readonly authenticatorData attribute");
+    // TODO: parseAuthenticatorData() and make sure flags are correct
 }
 
 //************* BEGIN DELETE AFTER 1/1/2018 *************** //
@@ -370,8 +541,8 @@
 // note that the polyfill only gets loaded if navigator.credentials create doesn't exist
 // AND if the polyfill script is found at the right path (i.e. - the polyfill is opt-in)
 function ensureInterface() {
-    if (typeof navigator.credentials.create !== "function") {
-        debug = console.log;
+    if (typeof navigator.credentials === "object" && typeof navigator.credentials.create !== "function") {
+        // debug = onsole.log;
 
         return loadJavaScript("/webauthn/webauthn-polyfill/webauthn-polyfill.js")
             .then(() => {
diff --git a/webauthn/securecontext.http.html b/webauthn/securecontext.http.html
index 82464bd..5278695 100644
--- a/webauthn/securecontext.http.html
+++ b/webauthn/securecontext.http.html
@@ -17,7 +17,7 @@
     // Example 1
     // http://example.com/ opened in a top-level browsing context is not a secure context, as it was not delivered over an authenticated and encrypted channel.
     test (() => {
-        assert_false (typeof navigator.credentials.create === "function");
+        assert_false (typeof navigator.credentials === "object" && typeof navigator.credentials.create === "function");
     }, "no navigator.credentials.create in non-secure context");
 
     // Example 4: TODO
diff --git a/webauthn/securecontext.https.html b/webauthn/securecontext.https.html
index 9810a7f..6c9aabd 100644
--- a/webauthn/securecontext.https.html
+++ b/webauthn/securecontext.https.html
@@ -17,7 +17,7 @@
     // Example 2
     // https://example.com/ opened in a top-level browsing context is a secure context, as it was delivered over an authenticated and encrypted channel.
     test (() => {
-        assert_true (typeof navigator.credentials.create === "function");
+        assert_true (typeof navigator.credentials === "object" && typeof navigator.credentials.create === "function");
     }, "navigator.credentials.create exists in secure context");
 
     // Example 3: TODO
diff --git a/webdriver/tests/actions/mouse.py b/webdriver/tests/actions/mouse.py
index bb7e145..f852653 100644
--- a/webdriver/tests/actions/mouse.py
+++ b/webdriver/tests/actions/mouse.py
@@ -1,6 +1,6 @@
 import pytest
 
-from tests.actions.support.mouse import get_center
+from tests.actions.support.mouse import get_inview_center, get_viewport_rect
 from tests.actions.support.refine import get_events, filter_dict
 from tests.support.asserts import assert_move_to_coordinates
 from tests.support.inline import inline
@@ -12,11 +12,6 @@
     return inline(content)
 
 
-# TODO use pytest.approx once we upgrade to pytest > 3.0
-def approx(n, m, tolerance=1):
-    return abs(n - m) <= tolerance
-
-
 def test_click_at_coordinates(session, test_actions_page, mouse_chain):
     div_point = {
         "x": 82,
@@ -35,7 +30,7 @@
         assert e["button"] == 0
     expected = [
         {"type": "mousedown", "buttons": 1},
-        {"type": "mouseup",  "buttons": 0},
+        {"type": "mouseup", "buttons": 0},
         {"type": "click", "buttons": 0},
     ]
     filtered_events = [filter_dict(e, expected[0]) for e in events]
@@ -55,7 +50,7 @@
     events = get_events(session)
     expected = [
         {"type": "mousedown", "button": 2},
-        {"type": "contextmenu",  "button": 2},
+        {"type": "contextmenu", "button": 2},
     ]
     assert len(events) == 4
     filtered_events = [filter_dict(e, expected[0]) for e in events]
@@ -68,7 +63,7 @@
 
 def test_click_element_center(session, test_actions_page, mouse_chain):
     outer = session.find.css("#outer", all=False)
-    center = get_center(outer.rect)
+    center = get_inview_center(outer.rect, get_viewport_rect(session))
     mouse_chain.click(element=outer).perform()
     events = get_events(session)
     assert len(events) == 4
@@ -76,8 +71,8 @@
     assert ["mousemove", "mousedown", "mouseup", "click"] == event_types
     for e in events:
         if e["type"] != "mousemove":
-            assert approx(e["pageX"], center["x"])
-            assert approx(e["pageY"], center["y"])
+            assert pytest.approx(e["pageX"], center["x"])
+            assert pytest.approx(e["pageY"], center["y"])
             assert e["target"] == "outer"
 
 
@@ -102,8 +97,9 @@
 
 
 @pytest.mark.parametrize("drag_duration", [0, 300, 800])
-@pytest.mark.parametrize("dx, dy",
-    [(20, 0), (0, 15), (10, 15), (-20, 0), (10, -15), (-10, -15)])
+@pytest.mark.parametrize("dx, dy", [
+    (20, 0), (0, 15), (10, 15), (-20, 0), (10, -15), (-10, -15)
+])
 def test_drag_and_drop(session,
                        test_actions_page,
                        mouse_chain,
@@ -112,7 +108,7 @@
                        drag_duration):
     drag_target = session.find.css("#dragTarget", all=False)
     initial_rect = drag_target.rect
-    initial_center = get_center(initial_rect)
+    initial_center = get_inview_center(initial_rect, get_viewport_rect(session))
     # Conclude chain with extra move to allow time for last queued
     # coordinate-update of drag_target and to test that drag_target is "dropped".
     mouse_chain \
@@ -125,8 +121,8 @@
     # mouseup that ends the drag is at the expected destination
     e = get_events(session)[1]
     assert e["type"] == "mouseup"
-    assert approx(e["pageX"], initial_center["x"] + dx)
-    assert approx(e["pageY"], initial_center["y"] + dy)
+    assert pytest.approx(e["pageX"], initial_center["x"] + dx)
+    assert pytest.approx(e["pageY"], initial_center["y"] + dy)
     # check resulting location of the dragged element
     final_rect = drag_target.rect
     assert initial_rect["x"] + dx == final_rect["x"]
diff --git a/webdriver/tests/actions/mouse_dblclick.py b/webdriver/tests/actions/mouse_dblclick.py
index e9e4c26..bdd3905 100644
--- a/webdriver/tests/actions/mouse_dblclick.py
+++ b/webdriver/tests/actions/mouse_dblclick.py
@@ -1,6 +1,6 @@
 import pytest
 
-from tests.actions.support.mouse import get_center
+from tests.actions.support.mouse import get_inview_center, get_viewport_rect
 from tests.actions.support.refine import get_events, filter_dict
 from tests.support.asserts import assert_move_to_coordinates
 
@@ -63,7 +63,7 @@
 
 def test_dblclick_with_pause_after_second_pointerdown(dblclick_session, mouse_chain):
         outer = dblclick_session.find.css("#outer", all=False)
-        center = get_center(outer.rect)
+        center = get_inview_center(outer.rect, get_viewport_rect(dblclick_session))
         mouse_chain \
             .pointer_move(int(center["x"]), int(center["y"])) \
             .click() \
@@ -88,7 +88,7 @@
 
 def test_no_dblclick(dblclick_session, mouse_chain):
         outer = dblclick_session.find.css("#outer", all=False)
-        center = get_center(outer.rect)
+        center = get_inview_center(outer.rect, get_viewport_rect(dblclick_session))
         mouse_chain \
             .pointer_move(int(center["x"]), int(center["y"])) \
             .click() \
diff --git a/webdriver/tests/actions/pointer_origin.py b/webdriver/tests/actions/pointer_origin.py
new file mode 100644
index 0000000..cad59f0
--- /dev/null
+++ b/webdriver/tests/actions/pointer_origin.py
@@ -0,0 +1,129 @@
+import pytest
+
+from webdriver import MoveTargetOutOfBoundsException
+
+from tests.actions.support.mouse import get_inview_center, get_viewport_rect
+from tests.support.inline import inline
+
+
+def origin_doc(inner_style, outer_style=""):
+    return inline("""
+      <div id="outer" style="{1}"
+           onmousemove="window.coords = {{x: event.clientX, y: event.clientY}}">
+        <div id="inner" style="{0}"></div>
+      </div>
+    """.format(inner_style, outer_style))
+
+
+def get_click_coordinates(session):
+    return session.execute_script("return window.coords;")
+
+
+def test_viewport_inside(session, mouse_chain):
+    point = {"x": 50, "y": 50}
+
+    session.url = origin_doc("width: 100px; height: 50px; background: green;")
+    mouse_chain \
+        .pointer_move(point["x"], point["y"], origin="viewport") \
+        .perform()
+
+    click_coords = session.execute_script("return window.coords;")
+    assert pytest.approx(click_coords["x"], point["x"])
+    assert pytest.approx(click_coords["y"], point["y"])
+
+
+def test_viewport_outside(session, mouse_chain):
+    with pytest.raises(MoveTargetOutOfBoundsException):
+        mouse_chain \
+            .pointer_move(-50, -50, origin="viewport") \
+            .perform()
+
+
+def test_pointer_inside(session, mouse_chain):
+    start_point = {"x": 50, "y": 50}
+    offset = {"x": 10, "y": 5}
+
+    session.url = origin_doc("width: 100px; height: 50px; background: green;")
+    mouse_chain \
+        .pointer_move(start_point["x"], start_point["y"]) \
+        .pointer_move(offset["x"], offset["y"], origin="pointer") \
+        .perform()
+
+    click_coords = session.execute_script("return window.coords;")
+    assert pytest.approx(click_coords["x"], start_point["x"] + offset["x"])
+    assert pytest.approx(click_coords["y"], start_point["y"] + offset["y"])
+
+
+def test_pointer_outside(session, mouse_chain):
+    with pytest.raises(MoveTargetOutOfBoundsException):
+        mouse_chain \
+            .pointer_move(-50, -50, origin="pointer") \
+            .perform()
+
+
+def test_element_center_point(session, mouse_chain):
+    session.url = origin_doc("width: 100px; height: 50px; background: green;")
+    elem = session.find.css("#inner", all=False)
+    center = get_inview_center(elem.rect, get_viewport_rect(session))
+
+    mouse_chain \
+        .pointer_move(0, 0, origin=elem) \
+        .perform()
+
+    click_coords = get_click_coordinates(session)
+    assert pytest.approx(click_coords["x"], center["x"])
+    assert pytest.approx(click_coords["y"], center["y"])
+
+
+def test_element_center_point_with_offset(session, mouse_chain):
+    session.url = origin_doc("width: 100px; height: 50px; background: green;")
+    elem = session.find.css("#inner", all=False)
+    center = get_inview_center(elem.rect, get_viewport_rect(session))
+
+    mouse_chain \
+        .pointer_move(10, 15, origin=elem) \
+        .perform()
+
+    click_coords = get_click_coordinates(session)
+    assert pytest.approx(click_coords["x"], center["x"] + 10)
+    assert pytest.approx(click_coords["y"], center["y"] + 15)
+
+
+def test_element_in_view_center_point_partly_visible(session, mouse_chain):
+    session.url = origin_doc("""width: 100px; height: 50px; background: green;
+                                position: relative; left: -50px; top: -25px;""")
+    elem = session.find.css("#inner", all=False)
+    center = get_inview_center(elem.rect, get_viewport_rect(session))
+
+    mouse_chain \
+        .pointer_move(0, 0, origin=elem) \
+        .perform()
+
+    click_coords = get_click_coordinates(session)
+    assert pytest.approx(click_coords["x"], center["x"])
+    assert pytest.approx(click_coords["y"], center["y"])
+
+
+def test_element_larger_than_viewport(session, mouse_chain):
+    session.url = origin_doc("width: 300vw; height: 300vh; background: green;")
+    elem = session.find.css("#inner", all=False)
+    center = get_inview_center(elem.rect, get_viewport_rect(session))
+
+    mouse_chain \
+        .pointer_move(0, 0, origin=elem) \
+        .perform()
+
+    click_coords = get_click_coordinates(session)
+    assert pytest.approx(click_coords["x"], center["x"])
+    assert pytest.approx(click_coords["y"], center["y"])
+
+
+def test_element_outside_of_view_port(session, mouse_chain):
+    session.url = origin_doc("""width: 100px; height: 50px; background: green;
+                                position: relative; left: -200px; top: -100px;""")
+    elem = session.find.css("#inner", all=False)
+
+    with pytest.raises(MoveTargetOutOfBoundsException):
+        mouse_chain \
+            .pointer_move(0, 0, origin=elem) \
+            .perform()
diff --git a/webdriver/tests/actions/support/mouse.py b/webdriver/tests/actions/support/mouse.py
index d627ebb..b3672eb 100644
--- a/webdriver/tests/actions/support/mouse.py
+++ b/webdriver/tests/actions/support/mouse.py
@@ -1,5 +1,26 @@
-def get_center(rect):
+def get_viewport_rect(session):
+    return session.execute_script("""
+        return {
+          height: window.innerHeight || document.documentElement.clientHeight,
+          width: window.innerWidth || document.documentElement.clientWidth,
+        };
+    """)
+
+
+def get_inview_center(elem_rect, viewport_rect):
+    x = {
+        "left": max(0, min(elem_rect["x"], elem_rect["x"] + elem_rect["width"])),
+        "right": min(viewport_rect["width"], max(elem_rect["x"],
+                                                 elem_rect["x"] + elem_rect["width"])),
+    }
+
+    y = {
+        "top": max(0, min(elem_rect["y"], elem_rect["y"] + elem_rect["height"])),
+        "bottom": min(viewport_rect["height"], max(elem_rect["y"],
+                                                   elem_rect["y"] + elem_rect["height"])),
+    }
+
     return {
-        "x": rect["width"] / 2 + rect["x"],
-        "y": rect["height"] / 2 + rect["y"],
+        "x": (x["left"] + x["right"]) / 2,
+        "y": (y["top"] + y["bottom"]) / 2,
     }
diff --git a/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https.html b/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https.html
index 8a40f9a..06408d0 100644
--- a/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https.html
+++ b/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https.html
@@ -14,7 +14,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(2)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       return sender.replaceTrack(tracks[1])
       .then(t.step_func(() => {
         assert_equals(sender.track, tracks[1]);
@@ -30,7 +30,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(1)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       return sender.replaceTrack(null)
       .then(t.step_func(() => {
         assert_equals(sender.track, null);
@@ -46,7 +46,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(2)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       assert_equals(sender.track, tracks[0]);
       sender.replaceTrack(tracks[1]);
       // replaceTrack() is asynchronous, there should be no synchronously
@@ -64,7 +64,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(2)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       caller.close();
       return sender.replaceTrack(tracks[1])
       .then(t.step_func(() => {
@@ -86,7 +86,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(2)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       caller.removeTrack(sender);
       // replaceTrack() should fail because the sender should be inactive after
       // removeTrack().
@@ -110,7 +110,7 @@
     const caller = new RTCPeerConnection();
     return getUserMediaTracksAndStreams(2)
     .then(t.step_func(([tracks, streams]) => {
-      const sender = caller.addTrack(tracks[0]);
+      const sender = caller.addTrack(tracks[0], streams[0]);
       let p = sender.replaceTrack(tracks[1])
       caller.removeTrack(sender);
       // replaceTrack() should fail because it executes steps in parallel and