Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Tue, 09 Oct 2018 07:05:46 +0300
changeset 495857 ac590db9a29403e4d42559f394f7ffab7d2c2eb5
parent 495856 80f3e8b1d66fb5a9dbaba457ae2535a679ec49c5 (current diff)
parent 495853 6a6c984745eff7fdcaeb6ec930e9c0669abaab9a (diff)
child 495858 a9db3033d9cbf95c87e8c26c361ffe558ef6e3a9
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
browser/config/mozconfigs/linux32/opt-dmd
browser/config/mozconfigs/linux64/opt-dmd
browser/config/mozconfigs/macosx64/opt-dmd
browser/config/mozconfigs/win32/opt-dmd
browser/config/mozconfigs/win64/opt-dmd
testing/web-platform/meta/css/css-contain/contain-size-grid-001.html.ini
testing/web-platform/meta/eventsource/format-field-id-2.htm.ini
testing/web-platform/meta/eventsource/format-field-id.htm.ini
testing/web-platform/meta/fetch/api/headers/header-values.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html.ini
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt
testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -119,17 +119,17 @@ class NetErrorChild extends ActorChild {
         // We only want to measure MitM rates for now. Treat it as unkown issuer.
         case MOZILLA_PKIX_ERROR_MITM_DETECTED:
         case SEC_ERROR_UNKNOWN_ISSUER:
           let brandName = gBrandBundle.GetStringFromName("brandShortName");
           if (newErrorPagesEnabled) {
             msg1 = "";
             msg1 += gPipNSSBundle.formatStringFromName("certErrorTrust_UnknownIssuer4", [hostString], 1);
             msg1 += "\n\n";
-            msg1 += gPipNSSBundle.formatStringFromName("certErrorTrust_UnknownIssuer5", [brandName, hostString], 2);
+            msg1 += gPipNSSBundle.formatStringFromName("certErrorTrust_UnknownIssuer6", [brandName, hostString], 2);
             msg1 += "\n\n";
           } else {
             msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
             msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
             msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
           }
           break;
         case SEC_ERROR_CA_CERT_INVALID:
@@ -163,17 +163,17 @@ class NetErrorChild extends ActorChild {
       let subjectAltNames = input.data.certSubjectAltNames.split(",");
       let numSubjectAltNames = subjectAltNames.length;
       let msgPrefix = "";
       if (numSubjectAltNames != 0) {
         if (numSubjectAltNames == 1) {
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
             let brandName = gBrandBundle.GetStringFromName("brandShortName");
-            msgPrefix = gPipNSSBundle.formatStringFromName("certErrorMismatchSinglePrefix1", [brandName, hostString], 2) + " ";
+            msgPrefix = gPipNSSBundle.formatStringFromName("certErrorMismatchSinglePrefix2", [brandName, hostString], 2) + " ";
             msgPrefix += gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
           } else {
             msgPrefix = gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
           }
           // Let's check if we want to make this a link.
           let okHost = input.data.certSubjectAltNames;
           let href = "";
           let thisHost = doc.location.hostname;
@@ -237,34 +237,34 @@ class NetErrorChild extends ActorChild {
             technicalInfo.appendChild(fragment);
           }
           technicalInfo.append("\n");
         } else {
           let msg = "";
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
             let brandName = gBrandBundle.GetStringFromName("brandShortName");
-            msg = gPipNSSBundle.formatStringFromName("certErrorMismatchMultiple1", [brandName, hostString], 2) + " ";
+            msg = gPipNSSBundle.formatStringFromName("certErrorMismatchMultiple2", [brandName, hostString], 2) + " ";
           } else {
             msg = gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
           }
           for (let i = 0; i < numSubjectAltNames; i++) {
             msg += subjectAltNames[i];
             if (i != (numSubjectAltNames - 1)) {
               msg += ", ";
             }
           }
           technicalInfo.append(msg + "\n");
         }
       } else {
         let msg = "";
         if (newErrorPagesEnabled) {
           technicalInfo.textContent = "";
           let brandName = gBrandBundle.GetStringFromName("brandShortName");
-          msg = gPipNSSBundle.formatStringFromName("certErrorMismatch1", [brandName, hostString], 2) + " ";
+          msg = gPipNSSBundle.formatStringFromName("certErrorMismatch2", [brandName, hostString], 2) + " ";
         } else {
           msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
                                                      [hostString], 1);
         }
         technicalInfo.append(msg + "\n");
       }
     }
 
@@ -272,43 +272,43 @@ class NetErrorChild extends ActorChild {
       let nowTime = new Date().getTime() * 1000;
       let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
       let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
       let msg = "";
       if (input.data.validity.notBefore) {
         if (nowTime > input.data.validity.notAfter) {
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
-            msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow1",
+            msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow2",
                                                     [hostString], 1);
             msg += "\n";
           } else {
             msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
                                                       [input.data.validity.notAfterLocalTime, now], 2);
             msg += "\n";
           }
         } else {
           // eslint-disable-next-line no-lonely-if
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
-            msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow1",
+            msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow2",
                                                       [hostString], 1);
             msg += "\n";
           } else {
             msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
                                                       [input.data.validity.notBeforeLocalTime, now], 2);
             msg += "\n";
           }
          }
         } else {
         // If something goes wrong, we assume the cert expired.
         // eslint-disable-next-line no-lonely-if
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
-            msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow1",
+            msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow2",
                                                       [hostString], 1);
             msg += "\n";
           } else {
             msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
                                                       ["", now], 2);
             msg += "\n";
           }
       }
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -177,17 +177,17 @@ add_task(async function checkBadStsCert(
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
       let advancedButton = doc.getElementById("advancedButton");
       advancedButton.click();
       return doc.getElementById("badCertTechnicalInfo").textContent;
     });
     if (Services.prefs.getBoolPref("browser.security.newcerterrorpage.enabled", false)) {
       ok(message.includes("SSL_ERROR_BAD_CERT_DOMAIN"), "Didn't find SSL_ERROR_BAD_CERT_DOMAIN.");
       ok(message.includes("The certificate is only valid for"), "Didn't find error message.");
-      ok(message.includes("a security certificate that is not valid for"), "Didn't find error message.");
+      ok(message.includes("a certificate that is not valid for"), "Didn't find error message.");
       ok(message.includes("badchain.include-subdomains.pinning.example.com"), "Didn't find domain in error message.");
 
       BrowserTestUtils.removeTab(gBrowser.selectedTab);
       return;
     }
     ok(message.includes("SSL_ERROR_BAD_CERT_DOMAIN"), "Didn't find SSL_ERROR_BAD_CERT_DOMAIN.");
     ok(message.includes("The certificate is only valid for"), "Didn't find error message.");
     ok(message.includes("uses an invalid security certificate"), "Didn't find error message.");
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/nightly
@@ -1,7 +1,8 @@
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-verify-mar
+ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
deleted file mode 100644
--- a/browser/config/mozconfigs/linux32/opt-dmd
+++ /dev/null
@@ -1,3 +0,0 @@
-ac_add_options --enable-dmd
-
-. "$topsrcdir/browser/config/mozconfigs/linux32/nightly"
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,10 +1,11 @@
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
 ac_add_options --enable-valgrind
+ac_add_options --disable-dmd
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 ac_add_options --disable-gtest-in-build
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/code-coverage
+++ b/browser/config/mozconfigs/linux64/code-coverage
@@ -2,16 +2,17 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
 TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
 
 ac_add_options --disable-install-strip
 ac_add_options --disable-elf-hack
 ac_add_options --disable-sandbox
+ac_add_options --disable-dmd
 ac_add_options --disable-profiling
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 
 export CFLAGS="--coverage"
 export CXXFLAGS="--coverage"
 export LDFLAGS="--coverage -L$topsrcdir/clang/lib/clang/7.0.0/lib/linux/"
 export LIBS="-lclang_rt.profile-x86_64"
--- a/browser/config/mozconfigs/linux64/fuzzing-ccov
+++ b/browser/config/mozconfigs/linux64/fuzzing-ccov
@@ -1,15 +1,16 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
 ac_add_options --disable-install-strip
 ac_add_options --disable-elf-hack
 ac_add_options --disable-sandbox
+ac_add_options --disable-dmd
 ac_add_options --disable-profiling
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 
 export CFLAGS="--coverage"
 export CXXFLAGS="--coverage"
 export LDFLAGS="--coverage"
 ac_add_options --enable-fuzzing
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/nightly
@@ -1,7 +1,8 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-verify-mar
+ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
deleted file mode 100644
--- a/browser/config/mozconfigs/linux64/opt-dmd
+++ /dev/null
@@ -1,3 +0,0 @@
-ac_add_options --enable-dmd
-
-. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,10 +1,11 @@
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
 ac_add_options --enable-valgrind
+ac_add_options --disable-dmd
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 ac_add_options --disable-gtest-in-build
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/nightly
@@ -1,13 +1,14 @@
 . "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
 
 ac_add_options --disable-install-strip
 ac_add_options --enable-verify-mar
 ac_add_options --enable-instruments
+ac_add_options --enable-dmd
 
 # Cross-compiled builds fail when dtrace is enabled
 if test `uname -s` != Linux; then
   ac_add_options --enable-dtrace
 fi
 
 ac_add_options --enable-lto
 
deleted file mode 100644
--- a/browser/config/mozconfigs/macosx64/opt-dmd
+++ /dev/null
@@ -1,5 +0,0 @@
-ac_add_options --enable-dmd
-
-. "$topsrcdir/browser/config/mozconfigs/macosx64/nightly"
-
-ac_add_options --disable-lto
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -4,16 +4,17 @@ whitelist = {
     'release': {},
     'nightly': {},
     }
 
 all_platforms = ['win64', 'win32', 'linux32', 'linux64', 'macosx64']
 
 for platform in all_platforms:
     whitelist['nightly'][platform] = [
+        'ac_add_options --enable-dmd',
         'ac_add_options --with-branding=browser/branding/nightly',
     ]
 
 whitelist['nightly']['macosx64'] += [
     'ac_add_options --disable-install-strip',
     'ac_add_options --enable-instruments',
     'ac_add_options --enable-dtrace',
     'if test `uname -s` != Linux; then',
--- a/browser/config/mozconfigs/win32/clang
+++ b/browser/config/mozconfigs/win32/clang
@@ -5,16 +5,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-optimize
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
-
-# Regardless of what mozconfig.vs-latest sets, lld-link that comes with the old
-# clang version used for static analysis fails to link multiple things. So
-# until bug 1427808 is resolved, use the Microsoft linker.
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
 export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win32/clang-debug
+++ b/browser/config/mozconfigs/win32/clang-debug
@@ -6,16 +6,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-optimize
 ac_add_options --enable-debug
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
-
-# Regardless of what mozconfig.vs-latest sets, lld-link that comes with the old
-# clang version used for static analysis fails to link multiple things. So
-# until bug 1427808 is resolved, use the Microsoft linker.
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
 export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -1,8 +1,9 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 ac_add_options --enable-verify-mar
+ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
deleted file mode 100644
--- a/browser/config/mozconfigs/win32/opt-dmd
+++ /dev/null
@@ -1,3 +0,0 @@
-ac_add_options --enable-dmd
-
-. "$topsrcdir/browser/config/mozconfigs/win32/nightly"
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -1,9 +1,10 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 ac_add_options --enable-verify-mar
+ac_add_options --enable-dmd
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
deleted file mode 100644
--- a/browser/config/mozconfigs/win64/opt-dmd
+++ /dev/null
@@ -1,3 +0,0 @@
-ac_add_options --enable-dmd
-
-. "$topsrcdir/browser/config/mozconfigs/win64/nightly"
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-win32-st-an.json
@@ -0,0 +1,22 @@
+{
+    "llvm_revision": "317840",
+    "stages": "3",
+    "build_libcxx": false,
+    "build_type": "Release",
+    "assertions": false,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "python_path": "c:/mozilla-build/python/python.exe",
+    "cc": "cl.exe",
+    "cxx": "cl.exe",
+    "patches": [
+      "r318309.patch",
+      "r320462.patch",
+      "msvc-host-x64.patch",
+      "aarch64-vastart-checking.patch",
+      "loosen-msvc-detection.patch"
+    ]
+}
new file mode 100644
--- /dev/null
+++ b/build/build-clang/msvc-host-x64.patch
@@ -0,0 +1,17 @@
+When looking for a linker, 32-bit clang-cl.exe wants to use the 32-bit-native link.exe located in Hostx86/x86, but this executable does not exist in our releng package, because we only use 64-bit-host toolchains.
+
+This patch makes clang-cl use the Hostx64/x86 linker instead. Ideally we wouldn't be using 32-bit clang-cl.exe in the first place. Bug 1414287 is on file to do so and remove this hack.
+
+diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
+--- a/clang/lib/Driver/ToolChains/MSVC.cpp
++++ b/clang/lib/Driver/ToolChains/MSVC.cpp
+@@ -817,8 +816,7 @@
+   switch (Type) {
+   case SubDirectoryType::Bin:
+     if (VSLayout == ToolsetLayout::VS2017OrNewer) {
+-      const bool HostIsX64 =
+-          llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit();
++      const bool HostIsX64 = true;
+       const char *const HostName = HostIsX64 ? "HostX64" : "HostX86";
+       llvm::sys::path::append(Path, "bin", HostName, SubdirName);
+     } else { // OlderVS or DevDivInternal
--- a/build/win32/mozconfig.vs2017
+++ b/build/win32/mozconfig.vs2017
@@ -16,19 +16,16 @@ if [ -d "${VSPATH}" ]; then
 
     export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.17134.0/ucrt:${VSPATH}/SDK/Include/10.0.17134.0/shared:${VSPATH}/SDK/Include/10.0.17134.0/um:${VSPATH}/SDK/Include/10.0.17134.0/winrt:${VSPATH}/DIA SDK/include"
     export LIB="${VSPATH}/VC/lib/x86:${VSPATH}/VC/atlmfc/lib/x86:${VSPATH}/SDK/Lib/10.0.17134.0/ucrt/x86:${VSPATH}/SDK/Lib/10.0.17134.0/um/x86:${VSPATH}/DIA SDK/lib"
 
     export WIN64_LINK="${VSPATH}/VC/bin/Hostx64/x64/link.exe"
     export WIN64_LIB="${VSPATH}/VC/lib/x64:${VSPATH}/VC/atlmfc/lib/x64:${VSPATH}/SDK/Lib/10.0.17134.0/ucrt/x64:${VSPATH}/SDK/Lib/10.0.17134.0/um/x64:${VSPATH}/DIA SDK/lib/amd64"
 fi
 
-ac_add_options --target=i686-pc-mingw32
-ac_add_options --host=x86_64-pc-mingw32
-
 . $topsrcdir/build/mozconfig.vs-common
 
 mk_export_correct_style WINDOWSSDKDIR
 mk_export_correct_style WIN32_REDIST_DIR
 mk_export_correct_style WIN_UCRT_REDIST_DIR
 mk_export_correct_style WIN_DIA_SDK_BIN_DIR
 mk_export_correct_style PATH
 mk_export_correct_style INCLUDE
--- a/config/config.mk
+++ b/config/config.mk
@@ -196,23 +196,16 @@ COMPILE_CMFLAGS = $(MOZ_LTO_CFLAGS) $(OS
 COMPILE_CMMFLAGS = $(MOZ_LTO_CFLAGS) $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
 ASFLAGS = $(COMPUTED_ASFLAGS)
 SFLAGS = $(COMPUTED_SFLAGS)
 
 HOST_CFLAGS = $(COMPUTED_HOST_CFLAGS) $(_DEPEND_CFLAGS)
 HOST_CXXFLAGS = $(COMPUTED_HOST_CXXFLAGS) $(_DEPEND_CFLAGS)
 HOST_C_LDFLAGS = $(COMPUTED_HOST_C_LDFLAGS)
 HOST_CXX_LDFLAGS = $(COMPUTED_HOST_CXX_LDFLAGS)
-# Win32 Cross-builds on win64 need to override LIB when invoking the linker,
-# which we do for rust through cargo-linker.bat, so we abuse it here.
-# Ideally, we'd empty LIB and pass -LIBPATH options to the linker somehow but
-# we don't have this in place for rust, so...
-ifdef WIN64_CARGO_LINKER
-HOST_LINKER = $(topobjdir)/build/win64/cargo-linker.bat
-endif
 
 ifdef MOZ_LTO
 ifeq (Darwin,$(OS_TARGET))
 # When linking on macOS, debug info is not linked along with the final binary,
 # and the dwarf data stays in object files until they are "linked" with the
 # dsymutil tool.
 # With LTO, object files are temporary, and are not kept around, which
 # means there's no object file for dsymutil to do its job. Consequently,
--- a/devtools/client/inspector/grids/components/Grid.js
+++ b/devtools/client/inspector/grids/components/Grid.js
@@ -38,38 +38,39 @@ class Grid extends PureComponent {
       onToggleShowGridAreas: PropTypes.func.isRequired,
       onToggleShowGridLineNumbers: PropTypes.func.isRequired,
       onToggleShowInfiniteLines: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   render() {
+    if (!this.props.grids.length) {
+      return (
+        dom.div({ className: "devtools-sidepanel-no-result" },
+          getStr("layout.noGridsOnThisPage")
+        )
+      );
+    }
+
     const {
       getSwatchColorPickerTooltip,
       grids,
       highlighterSettings,
       onHideBoxModelHighlighter,
       onSetGridOverlayColor,
       onShowBoxModelHighlighterForNode,
       onShowGridOutlineHighlight,
       onToggleShowGridAreas,
       onToggleGridHighlighter,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
       setSelectedNode,
     } = this.props;
-
-    if (!grids.length) {
-      return (
-        dom.div({ className: "devtools-sidepanel-no-result" },
-          getStr("layout.noGridsOnThisPage")
-        )
-      );
-    }
+    const highlightedGrids = grids.filter(grid => grid.highlighted);
 
     return (
       dom.div({ id: "layout-grid-container" },
         dom.div({ className: "grid-content" },
           GridList({
             getSwatchColorPickerTooltip,
             grids,
             onHideBoxModelHighlighter,
@@ -80,18 +81,21 @@ class Grid extends PureComponent {
           }),
           GridDisplaySettings({
             highlighterSettings,
             onToggleShowGridAreas,
             onToggleShowGridLineNumbers,
             onToggleShowInfiniteLines,
           })
         ),
-        GridOutline({
-          grids,
-          onShowGridOutlineHighlight,
-        })
+        highlightedGrids.length === 1 ?
+          GridOutline({
+            grids,
+            onShowGridOutlineHighlight,
+          })
+          :
+          null
       )
     );
   }
 }
 
 module.exports = Grid;
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -39,62 +39,61 @@ const VIEWPORT_MAX_HEIGHT = 150;
 class GridOutline extends PureComponent {
   static get propTypes() {
     return {
       grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
       onShowGridOutlineHighlight: PropTypes.func.isRequired,
     };
   }
 
+  static getDerivedStateFromProps(props) {
+    const selectedGrid = props.grids.find(grid => grid.highlighted);
+
+    // Store the height of the grid container in the component state to prevent overflow
+    // issues. We want to store the width of the grid container as well so that the
+    // viewbox is only the calculated width of the grid outline.
+    const { width, height } = selectedGrid && selectedGrid.gridFragments.length
+                            ? getTotalWidthAndHeight(selectedGrid)
+                            : { width: 0, height: 0 };
+    let showOutline;
+
+    if (selectedGrid && selectedGrid.gridFragments.length) {
+      const { cols, rows } = selectedGrid.gridFragments[0];
+
+      // Show the grid outline if both the rows/columns are less than or equal
+      // to their max prefs.
+      showOutline = (cols.lines.length <= GRID_OUTLINE_MAX_COLUMNS_PREF) &&
+                    (rows.lines.length <= GRID_OUTLINE_MAX_ROWS_PREF);
+    }
+
+    return { height, width, selectedGrid, showOutline };
+  }
+
   constructor(props) {
     super(props);
 
     this.state = {
       height: 0,
       selectedGrid: null,
       showOutline: true,
       width: 0,
     };
 
     this.doHighlightCell = this.doHighlightCell.bind(this);
     this.getGridAreaName = this.getGridAreaName.bind(this);
     this.getHeight = this.getHeight.bind(this);
-    this.getTotalWidthAndHeight = this.getTotalWidthAndHeight.bind(this);
     this.onHighlightCell = this.onHighlightCell.bind(this);
     this.renderCannotShowOutlineText = this.renderCannotShowOutlineText.bind(this);
     this.renderGrid = this.renderGrid.bind(this);
     this.renderGridCell = this.renderGridCell.bind(this);
     this.renderGridOutline = this.renderGridOutline.bind(this);
     this.renderGridOutlineBorder = this.renderGridOutlineBorder.bind(this);
     this.renderOutline = this.renderOutline.bind(this);
   }
 
-  componentWillReceiveProps({ grids }) {
-    const selectedGrid = grids.find(grid => grid.highlighted);
-
-    // Store the height of the grid container in the component state to prevent overflow
-    // issues. We want to store the width of the grid container as well so that the
-    // viewbox is only the calculated width of the grid outline.
-    const { width, height } = selectedGrid && selectedGrid.gridFragments.length
-                            ? this.getTotalWidthAndHeight(selectedGrid)
-                            : { width: 0, height: 0 };
-    let showOutline;
-
-    if (selectedGrid && selectedGrid.gridFragments.length) {
-      const { cols, rows } = selectedGrid.gridFragments[0];
-
-      // Show the grid outline if both the rows/columns are less than or equal
-      // to their max prefs.
-      showOutline = (cols.lines.length <= GRID_OUTLINE_MAX_COLUMNS_PREF) &&
-                    (rows.lines.length <= GRID_OUTLINE_MAX_ROWS_PREF);
-    }
-
-    this.setState({ height, width, selectedGrid, showOutline });
-  }
-
   doHighlightCell(target, hide) {
     const {
       grids,
       onShowGridOutlineHighlight,
     } = this.props;
     const name = target.dataset.gridAreaName;
     const id = target.dataset.gridId;
     const gridFragmentIndex = target.dataset.gridFragmentIndex;
@@ -155,48 +154,16 @@ class GridOutline extends PureComponent 
     } else if (height <= VIEWPORT_MIN_HEIGHT) {
       return VIEWPORT_MIN_HEIGHT;
     }
 
     return height;
   }
 
   /**
-   * Get the width and height of a given grid.
-   *
-   * @param  {Object} grid
-   *         A single grid container in the document.
-   * @return {Object} An object like { width, height }
-   */
-  getTotalWidthAndHeight(grid) {
-    // TODO: We are drawing the first fragment since only one is currently being stored.
-    // In the future we will need to iterate over all fragments of a grid.
-    const { gridFragments } = grid;
-    const { rows, cols } = gridFragments[0];
-
-    let height = 0;
-    for (let i = 0; i < rows.lines.length - 1; i++) {
-      height += GRID_CELL_SCALE_FACTOR * (rows.tracks[i].breadth / 100);
-    }
-
-    let width = 0;
-    for (let i = 0; i < cols.lines.length - 1; i++) {
-      width += GRID_CELL_SCALE_FACTOR * (cols.tracks[i].breadth / 100);
-    }
-
-    // All writing modes other than horizontal-tb (the initial value) involve a 90 deg
-    // rotation, so swap width and height.
-    if (grid.writingMode != "horizontal-tb") {
-      [ width, height ] = [ height, width ];
-    }
-
-    return { width, height };
-  }
-
-  /**
    * Displays a message text "Cannot show outline for this grid".
    */
   renderCannotShowOutlineText() {
     return (
       dom.div({ className: "grid-outline-text" },
         dom.span(
           {
             className: "grid-outline-text-icon",
@@ -391,9 +358,41 @@ class GridOutline extends PureComponent 
         },
         this.renderOutline()
       )
       :
       null;
   }
 }
 
+/**
+ * Get the width and height of a given grid.
+ *
+ * @param  {Object} grid
+ *         A single grid container in the document.
+ * @return {Object} An object like { width, height }
+ */
+function getTotalWidthAndHeight(grid) {
+  // TODO: We are drawing the first fragment since only one is currently being stored.
+  // In the future we will need to iterate over all fragments of a grid.
+  const { gridFragments } = grid;
+  const { rows, cols } = gridFragments[0];
+
+  let height = 0;
+  for (let i = 0; i < rows.lines.length - 1; i++) {
+    height += GRID_CELL_SCALE_FACTOR * (rows.tracks[i].breadth / 100);
+  }
+
+  let width = 0;
+  for (let i = 0; i < cols.lines.length - 1; i++) {
+    width += GRID_CELL_SCALE_FACTOR * (cols.tracks[i].breadth / 100);
+  }
+
+  // All writing modes other than horizontal-tb (the initial value) involve a 90 deg
+  // rotation, so swap width and height.
+  if (grid.writingMode != "horizontal-tb") {
+    [ width, height ] = [ height, width ];
+  }
+
+  return { width, height };
+}
+
 module.exports = GridOutline;
--- a/devtools/client/inspector/grids/test/browser.ini
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -28,16 +28,17 @@ skip-if = (verify && (os == 'win' || os 
 [browser_grids_grid-list-toggle-grids_01.js]
 [browser_grids_grid-list-toggle-grids_02.js]
 [browser_grids_grid-list-toggle-multiple-grids.js]
 [browser_grids_grid-outline-cannot-show-outline.js]
 [browser_grids_grid-outline-highlight-area.js]
 skip-if = (verify && (os == 'win'))
 [browser_grids_grid-outline-highlight-cell.js]
 skip-if = (verify && (os == 'win'))
+[browser_grids_grid-outline-multiple-grids.js]
 [browser_grids_grid-outline-selected-grid.js]
 [browser_grids_grid-outline-updates-on-grid-change.js]
 [browser_grids_grid-outline-writing-mode.js]
 skip-if = (verify && (os == 'win'))
 [browser_grids_highlighter-setting-rules-grid-toggle.js]
 [browser_grids_number-of-css-grids-telemetry.js]
 [browser_grids_persist-color-palette.js]
 [browser_grids_restored-after-reload.js]
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js
@@ -17,16 +17,17 @@ const TEST_URI = `
   </div>
   <div id="grid2">
     <div class="cell1">cell1</div>
     <div class="cell2">cell2</div>
   </div>
 `;
 
 add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 1);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector, testActor } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { highlighters, store } = inspector;
 
   await selectNode("#grid", inspector);
   const gridList = doc.getElementById("grid-list");
   const checkbox1 = gridList.children[0].querySelector("input");
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-grids_02.js
@@ -18,16 +18,17 @@ const TEST_URI = `
   </div>
   <div id="grid2" class="grid">
     <div class="cell1">cell1</div>
     <div class="cell2">cell2</div>
   </div>
 `;
 
 add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 1);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { highlighters, store } = inspector;
 
   await selectNode("#grid1", inspector);
   const gridList = doc.getElementById("grid-list");
   const checkbox1 = gridList.children[0].querySelector("input");
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-multiple-grids.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid outline is not shown when more than one grid is highlighted.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 2);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, gridInspector } = await openLayoutView();
+  const { document: doc } = gridInspector;
+  const { highlighters, store } = inspector;
+
+  await selectNode("#grid1", inspector);
+  const gridList = doc.getElementById("grid-list");
+  const checkbox1 = gridList.children[0].querySelector("input");
+  const checkbox2 = gridList.children[1].querySelector("input");
+
+  info("Toggling ON the CSS grid highlighter for #grid1.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  const onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 2);
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length === 2 &&
+    state.grids[0].highlighted &&
+    !state.grids[1].highlighted);
+  checkbox1.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+  const elements = await onGridOutlineRendered;
+
+  info("Checking the grid outline for #grid1 is shown.");
+  ok(doc.getElementById("grid-outline-container"), "Grid outline container is rendered.");
+  is(elements.length, 2, "Grid outline is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid2.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length === 2 &&
+    state.grids[0].highlighted &&
+    state.grids[1].highlighted);
+  checkbox2.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Checking the grid outline is not shown.");
+  ok(!doc.getElementById("grid-outline-container"), "Grid outline is not rendered.");
+});
--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-writing-mode.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-writing-mode.js
@@ -56,16 +56,17 @@ const TEST_URI = `
   <div class="grid s-lr">
     <div id="cella">Cell A</div>
     <div id="cellb">Cell B</div>
     <div id="cellc">Cell C</div>
   </div>
 `;
 
 add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 1);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
 
   const { inspector, gridInspector } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { highlighters, store } = inspector;
 
   info("Checking the initial state of the Grid Inspector.");
   ok(!doc.getElementById("grid-outline-container"),
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
@@ -18,16 +18,17 @@ const TEST_URI = `
   </div>
   <div id="grid2" class="grid">
     <div class="cell1">cell1</div>
     <div class="cell2">cell2</div>
   </div>
 `;
 
 add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 1);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   info("Selecting the first grid container.");
   await selectNode("#grid1", inspector);
   let container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   let gridToggle = container.querySelector(".ruleview-grid");
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -69,17 +69,17 @@ pref("devtools.flexboxinspector.enabled"
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
 pref("devtools.gridinspector.gridOutlineMaxRows", 50);
 pref("devtools.gridinspector.showGridAreas", false);
 pref("devtools.gridinspector.showGridLineNumbers", false);
 pref("devtools.gridinspector.showInfiniteLines", false);
 // Max number of grid highlighters that can be displayed
-pref("devtools.gridinspector.maxHighlighters", 1);
+pref("devtools.gridinspector.maxHighlighters", 3);
 
 // Whether or not the box model panel is opened in the layout view
 pref("devtools.layout.boxmodel.opened", true);
 // Whether or not the flexbox panel is opened in the layout view
 pref("devtools.layout.flexbox.opened", true);
 // Whether or not the grid inspector panel is opened in the layout view
 pref("devtools.layout.grid.opened", true);
 
--- a/docshell/resources/content/jar.mn
+++ b/docshell/resources/content/jar.mn
@@ -1,6 +1,7 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 toolkit.jar:
 	content/global/netError.xhtml
+	content/global/netError.js
copy from docshell/resources/content/netError.xhtml
copy to docshell/resources/content/netError.js
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.js
@@ -1,39 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html [
-  <!ENTITY % htmlDTD
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "DTD/xhtml1-strict.dtd">
-  %htmlDTD;
-  <!ENTITY % netErrorAppDTD
-    SYSTEM "chrome://global/locale/netErrorApp.dtd">
-  %netErrorAppDTD;
-  <!ENTITY % netErrorDTD
-    SYSTEM "chrome://global/locale/netError.dtd">
-  %netErrorDTD;
-  <!ENTITY % globalDTD
-    SYSTEM "chrome://global/locale/global.dtd">
-  %globalDTD;
-]>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>&loadError.label;</title>
-    <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
-    <!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
-         toolkit/components/places/src/nsFaviconService.h should be updated. -->
-    <link rel="icon" id="favicon" href="chrome://global/skin/icons/warning.svg"/>
-
-    <script type="application/javascript"><![CDATA[
       // Error url MUST be formatted like this:
       //   moz-neterror:page?e=error&u=url&d=desc
       //
       // or optionally, to specify an alternate CSS class to allow for
       // custom styling and favicon:
       //
       //   moz-neterror:page?e=error&u=url&s=classname&d=desc
 
@@ -184,16 +153,19 @@
           // again won't help.
           document.getElementById("errorTryAgain").style.display = "none";
 
           var container = document.getElementById("errorLongDesc");
           for (var span of container.querySelectorAll("span.hostname")) {
             span.textContent = document.location.hostname;
           }
         }
+
+        if (document.getElementById("errorTryAgain").style.display != "none")
+          addAutofocus("errorTryAgain");
       }
 
       function showSecuritySection() {
         // Swap link out, content in
         document.getElementById('securityOverrideContent').style.display = '';
         document.getElementById('securityOverrideLink').style.display = 'none';
       }
 
@@ -272,129 +244,33 @@
         if (endsWith(thisHost, "." + okHost))
           link.href = proto + okHost;
       }
 
       function endsWith(haystack, needle) {
         return haystack.slice(-needle.length) == needle;
       }
 
-    ]]></script>
-  </head>
-
-  <body dir="&locale.dir;">
+      /* Only do autofocus if we're the toplevel frame; otherwise we
+         don't want to call attention to ourselves!  The key part is
+         that autofocus happens on insertion into the tree, so we
+         can remove the button, add @autofocus, and reinsert the
+         button.
+       */
+      function addAutofocus(buttonId, position = "afterbegin") {
+        if (window.top == window) {
+          var button = document.getElementById(buttonId);
+          var parent = button.parentNode;
+          button.remove();
+          button.setAttribute("autofocus", "true");
+          parent.insertAdjacentElement(position, button);
+        }
+      }
 
-    <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
-    <div id="errorContainer">
-      <div id="errorTitlesContainer">
-        <h1 id="et_generic">&generic.title;</h1>
-        <h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
-        <h1 id="et_fileNotFound">&fileNotFound.title;</h1>
-        <h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
-        <h1 id="et_malformedURI">&malformedURI.title;</h1>
-        <h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
-        <h1 id="et_connectionFailure">&connectionFailure.title;</h1>
-        <h1 id="et_netTimeout">&netTimeout.title;</h1>
-        <h1 id="et_redirectLoop">&redirectLoop.title;</h1>
-        <h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
-        <h1 id="et_netReset">&netReset.title;</h1>
-        <h1 id="et_notCached">&notCached.title;</h1>
-        <h1 id="et_netOffline">&netOffline.title;</h1>
-        <h1 id="et_netInterrupt">&netInterrupt.title;</h1>
-        <h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
-        <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
-        <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
-        <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
-        <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
-        <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
-        <h1 id="et_nssBadCert">&nssBadCert.title;</h1>
-        <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
-        <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
-        <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
-        <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
-        <h1 id="et_blockedByPolicy">&blockedByPolicy.title;</h1>
-      </div>
-      <div id="errorDescriptionsContainer">
-        <div id="ed_generic">&generic.longDesc;</div>
-        <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
-        <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
-        <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
-        <div id="ed_malformedURI">&malformedURI.longDesc;</div>
-        <div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
-        <div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
-        <div id="ed_netTimeout">&netTimeout.longDesc;</div>
-        <div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
-        <div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
-        <div id="ed_netReset">&netReset.longDesc;</div>
-        <div id="ed_notCached">&notCached.longDesc;</div>
-        <div id="ed_netOffline">&netOffline.longDesc2;</div>
-        <div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
-        <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
-        <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
-        <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
-        <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
-        <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
-        <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
-        <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
-        <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
-        <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
-        <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
-        <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
-        <div id="ed_blockedByPolicy"></div>
-      </div>
-    </div>
+      let errorTryAgain = document.getElementById("errorTryAgain");
+      errorTryAgain.addEventListener("click", function() {
+        retryThis(this);
+      });
 
-    <!-- PAGE CONTAINER (for styling purposes only) -->
-    <div id="errorPageContainer">
-
-      <!-- Error Title -->
-      <div id="errorTitle">
-        <h1 id="errorTitleText" />
-      </div>
-
-      <!-- LONG CONTENT (the section most likely to require scrolling) -->
-      <div id="errorLongContent">
-
-        <!-- Short Description -->
-        <div id="errorShortDesc">
-          <p id="errorShortDescText" />
-        </div>
-
-        <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
-        <div id="errorLongDesc" />
-
-        <!-- Override section - For ssl errors only.  Removed on init for other
-             error types.  -->
-        <div id="securityOverrideDiv">
-          <a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
-          <div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
-        </div>
-      </div>
-
-      <!-- Retry Button -->
-      <button id="errorTryAgain" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
-      <script>
-        // Only do autofocus if we're the toplevel frame; otherwise we
-        // don't want to call attention to ourselves!  The key part is
-        // that autofocus happens on insertion into the tree, so we
-        // can remove the button, add @autofocus, and reinsert the
-        // button.
-        if (window.top == window) {
-            var button = document.getElementById("errorTryAgain");
-            var nextSibling = button.nextSibling;
-            var parent = button.parentNode;
-            parent.removeChild(button);
-            button.setAttribute("autofocus", "true");
-            parent.insertBefore(button, nextSibling);
-        }
-      </script>
-
-    </div>
-
-    <!--
-    - Note: It is important to run the script this way, instead of using
-    - an onload handler. This is because error pages are loaded as
-    - LOAD_BACKGROUND, which means that onload handlers will not be executed.
-    -->
-    <script type="application/javascript">initPage();</script>
-
-  </body>
-</html>
+      // Note: It is important to run the script this way, instead of using
+      // an onload handler. This is because error pages are loaded as
+      // LOAD_BACKGROUND, which means that onload handlers will not be executed.
+      initPage();
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -17,272 +17,22 @@
 ]>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
+    <meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
     <title>&loadError.label;</title>
     <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
     <!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
          toolkit/components/places/src/nsFaviconService.h should be updated. -->
     <link rel="icon" id="favicon" href="chrome://global/skin/icons/warning.svg"/>
-
-    <script type="application/javascript"><![CDATA[
-      // Error url MUST be formatted like this:
-      //   moz-neterror:page?e=error&u=url&d=desc
-      //
-      // or optionally, to specify an alternate CSS class to allow for
-      // custom styling and favicon:
-      //
-      //   moz-neterror:page?e=error&u=url&s=classname&d=desc
-
-      // Note that this file uses document.documentURI to get
-      // the URL (with the format from above). This is because
-      // document.location.href gets the current URI off the docshell,
-      // which is the URL displayed in the location bar, i.e.
-      // the URI that the user attempted to load.
-
-      function getErrorCode()
-      {
-        var url = document.documentURI;
-        var error = url.search(/e\=/);
-        var duffUrl = url.search(/\&u\=/);
-        return decodeURIComponent(url.slice(error + 2, duffUrl));
-      }
-
-      function getCSSClass()
-      {
-        var url = document.documentURI;
-        var matches = url.match(/s\=([^&]+)\&/);
-        // s is optional, if no match just return nothing
-        if (!matches || matches.length < 2)
-          return "";
-
-        // parenthetical match is the second entry
-        return decodeURIComponent(matches[1]);
-      }
-
-      function getDescription()
-      {
-        var url = document.documentURI;
-        var desc = url.search(/d\=/);
-
-        // desc == -1 if not found; if so, return an empty string
-        // instead of what would turn out to be portions of the URI
-        if (desc == -1)
-          return "";
-
-        return decodeURIComponent(url.slice(desc + 2));
-      }
-
-      function retryThis(buttonEl)
-      {
-        // Note: The application may wish to handle switching off "offline mode"
-        // before this event handler runs, but using a capturing event handler.
-
-        // Session history has the URL of the page that failed
-        // to load, not the one of the error page. So, just call
-        // reload(), which will also repost POST data correctly.
-        try {
-          location.reload();
-        } catch (e) {
-          // We probably tried to reload a URI that caused an exception to
-          // occur;  e.g. a nonexistent file.
-        }
-
-        buttonEl.disabled = true;
-      }
-
-      function initPage()
-      {
-        var err = getErrorCode();
-
-        // if it's an unknown error or there's no title or description
-        // defined, get the generic message
-        var errTitle = document.getElementById("et_" + err);
-        var errDesc  = document.getElementById("ed_" + err);
-        if (!errTitle || !errDesc)
-        {
-          errTitle = document.getElementById("et_generic");
-          errDesc  = document.getElementById("ed_generic");
-        }
-
-        var title = document.getElementById("errorTitleText");
-        if (title)
-        {
-          title.parentNode.replaceChild(errTitle, title);
-          // change id to the replaced child's id so styling works
-          errTitle.id = "errorTitleText";
-        }
-
-        var sd = document.getElementById("errorShortDescText");
-        if (sd)
-          sd.textContent = getDescription();
-
-        var ld = document.getElementById("errorLongDesc");
-        if (ld)
-        {
-          ld.parentNode.replaceChild(errDesc, ld);
-          // change id to the replaced child's id so styling works
-          errDesc.id = "errorLongDesc";
-        }
-
-        // remove undisplayed errors to avoid bug 39098
-        var errContainer = document.getElementById("errorContainer");
-        errContainer.remove();
-
-        var className = getCSSClass();
-        if (className && className != "expertBadCert") {
-          // Associate a CSS class with the root of the page, if one was passed in,
-          // to allow custom styling.
-          // Not "expertBadCert" though, don't want to deal with the favicon
-          document.documentElement.className = className;
-
-          // Also, if they specified a CSS class, they must supply their own
-          // favicon.  In order to trigger the browser to repaint though, we
-          // need to remove/add the link element.
-          var favicon = document.getElementById("favicon");
-          var faviconParent = favicon.parentNode;
-          faviconParent.removeChild(favicon);
-          favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
-          faviconParent.appendChild(favicon);
-        }
-        if (className == "expertBadCert") {
-          showSecuritySection();
-        }
-
-        if (err == "remoteXUL") {
-          // Remove the "Try again" button for remote XUL errors given that
-          // it is useless.
-          document.getElementById("errorTryAgain").style.display = "none";
-        }
-
-        if (err == "cspBlocked") {
-          // Remove the "Try again" button for CSP violations, since it's
-          // almost certainly useless. (Bug 553180)
-          document.getElementById("errorTryAgain").style.display = "none";
-        }
-
-        if (err == "nssBadCert") {
-          // Remove the "Try again" button for security exceptions, since it's
-          // almost certainly useless.
-          document.getElementById("errorTryAgain").style.display = "none";
-          document.getElementById("errorPageContainer").setAttribute("class", "certerror");
-          addDomainErrorLink();
-        }
-        else {
-          // Remove the override block for non-certificate errors.  CSS-hiding
-          // isn't good enough here, because of bug 39098
-          var secOverride = document.getElementById("securityOverrideDiv");
-          secOverride.remove();
-        }
-
-        if (err == "inadequateSecurityError" || err == "blockedByPolicy") {
-          // Remove the "Try again" button from pages that don't need it.
-          // For HTTP/2 inadequate security or pages blocked by policy, trying
-          // again won't help.
-          document.getElementById("errorTryAgain").style.display = "none";
-
-          var container = document.getElementById("errorLongDesc");
-          for (var span of container.querySelectorAll("span.hostname")) {
-            span.textContent = document.location.hostname;
-          }
-        }
-      }
-
-      function showSecuritySection() {
-        // Swap link out, content in
-        document.getElementById('securityOverrideContent').style.display = '';
-        document.getElementById('securityOverrideLink').style.display = 'none';
-      }
-
-      /* In the case of SSL error pages about domain mismatch, see if
-         we can hyperlink the user to the correct site.  We don't want
-         to do this generically since it allows MitM attacks to redirect
-         users to a site under attacker control, but in certain cases
-         it is safe (and helpful!) to do so.  Bug 402210
-      */
-      function addDomainErrorLink() {
-        // Rather than textContent, we need to treat description as HTML
-        var sd = document.getElementById("errorShortDescText");
-        if (sd) {
-          var desc = getDescription();
-
-          // sanitize description text - see bug 441169
-
-          // First, find the index of the <a> tag we care about, being careful not to
-          // use an over-greedy regex
-          var re = /<a id="cert_domain_link" title="([^"]+)">/;
-          var result = re.exec(desc);
-          if(!result)
-            return;
-
-          // Remove sd's existing children
-          sd.textContent = "";
-
-          // Everything up to the link should be text content
-          sd.appendChild(document.createTextNode(desc.slice(0, result.index)));
-
-          // Now create the link itself
-          var anchorEl = document.createElement("a");
-          anchorEl.setAttribute("id", "cert_domain_link");
-          anchorEl.setAttribute("title", result[1]);
-          anchorEl.appendChild(document.createTextNode(result[1]));
-          sd.appendChild(anchorEl);
-
-          // Finally, append text for anything after the closing </a>
-          sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length)));
-        }
-
-        var link = document.getElementById('cert_domain_link');
-        if (!link)
-          return;
-
-        var okHost = link.getAttribute("title");
-        var thisHost = document.location.hostname;
-        var proto = document.location.protocol;
-
-        // If okHost is a wildcard domain ("*.example.com") let's
-        // use "www" instead.  "*.example.com" isn't going to
-        // get anyone anywhere useful. bug 432491
-        okHost = okHost.replace(/^\*\./, "www.");
-
-        /* case #1:
-         * example.com uses an invalid security certificate.
-         *
-         * The certificate is only valid for www.example.com
-         *
-         * Make sure to include the "." ahead of thisHost so that
-         * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
-         *
-         * We'd normally just use a RegExp here except that we lack a
-         * library function to escape them properly (bug 248062), and
-         * domain names are famous for having '.' characters in them,
-         * which would allow spurious and possibly hostile matches.
-         */
-        if (endsWith(okHost, "." + thisHost))
-          link.href = proto + okHost;
-
-        /* case #2:
-         * browser.garage.maemo.org uses an invalid security certificate.
-         *
-         * The certificate is only valid for garage.maemo.org
-         */
-        if (endsWith(thisHost, "." + okHost))
-          link.href = proto + okHost;
-      }
-
-      function endsWith(haystack, needle) {
-        return haystack.slice(-needle.length) == needle;
-      }
-
-    ]]></script>
   </head>
 
   <body dir="&locale.dir;">
 
     <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
     <div id="errorContainer">
       <div id="errorTitlesContainer">
         <h1 id="et_generic">&generic.title;</h1>
@@ -360,41 +110,19 @@
 
         <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
         <div id="errorLongDesc" />
 
         <!-- Override section - For ssl errors only.  Removed on init for other
              error types.  -->
         <div id="securityOverrideDiv">
           <a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
-          <div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
+          <div id="securityOverrideContent">&securityOverride.warningContent;</div>
         </div>
       </div>
 
       <!-- Retry Button -->
-      <button id="errorTryAgain" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
-      <script>
-        // Only do autofocus if we're the toplevel frame; otherwise we
-        // don't want to call attention to ourselves!  The key part is
-        // that autofocus happens on insertion into the tree, so we
-        // can remove the button, add @autofocus, and reinsert the
-        // button.
-        if (window.top == window) {
-            var button = document.getElementById("errorTryAgain");
-            var nextSibling = button.nextSibling;
-            var parent = button.parentNode;
-            parent.removeChild(button);
-            button.setAttribute("autofocus", "true");
-            parent.insertBefore(button, nextSibling);
-        }
-      </script>
-
+      <button id="errorTryAgain" autocomplete="off">&retry.label;</button>
     </div>
 
-    <!--
-    - Note: It is important to run the script this way, instead of using
-    - an onload handler. This is because error pages are loaded as
-    - LOAD_BACKGROUND, which means that onload handlers will not be executed.
-    -->
-    <script type="application/javascript">initPage();</script>
-
+    <script type="application/javascript" src="chrome://global/content/netError.js"/>
   </body>
 </html>
--- a/ipc/chromium/src/third_party/libevent/README.mozilla
+++ b/ipc/chromium/src/third_party/libevent/README.mozilla
@@ -19,19 +19,19 @@ Android, and BSD headers to be appropria
 Mac doesn't need this since only 64-bit is supported. Use __LP64__ to
 distinguish the two cases. If you get something wrong, the CHECK_EVENT_SIZEOF
 static assertions in message_pump_libevent.cc will fail. If a new constant is
 added, also add a static assertion for it to message_pump_libevent.cc.
 
 2. Apply the following patches from
 ipc/chromium/src/third_party/libevent/patches/:
 
-- "openbsd-no-arc4random_addrandom.patch". Fixes the build on OpenBSD
-  and Android (which don't provide arc4random_addrandom anymore).
-  See bug 931354 and bug 1259218.
+- "openbsd-no-arc4random_addrandom.patch". Fixes the build on OpenBSD,
+  FreeBSD and Android (which don't provide arc4random_addrandom anymore).
+  See bug 931354, bug 1259218, bug 1497169.
 
 - "dont-use-issetugid-on-android.patch". Fixes Android startup crashes.
   See bug 1030899.
 
 - "linux-no-sysctl.patch". Fixes the build on Linux systems without sysctl.h.
   See bug 1263429.
 
 - "android-api-level.patch". Fixes the build under clang or NDK r16.
--- a/ipc/chromium/src/third_party/libevent/evutil_rand.c
+++ b/ipc/chromium/src/third_party/libevent/evutil_rand.c
@@ -187,17 +187,17 @@ ev_arc4random_buf(void *buf, size_t n)
 #endif /* } !EVENT__HAVE_ARC4RANDOM */
 
 void
 evutil_secure_rng_get_bytes(void *buf, size_t n)
 {
 	ev_arc4random_buf(buf, n);
 }
 
-#if !defined(__OpenBSD__) && !defined(ANDROID)
+#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(ANDROID)
 void
 evutil_secure_rng_add_bytes(const char *buf, size_t n)
 {
 	arc4random_addrandom((unsigned char*)buf,
 	    n>(size_t)INT_MAX ? INT_MAX : (int)n);
 }
 #endif
 
--- a/ipc/chromium/src/third_party/libevent/include/event2/util.h
+++ b/ipc/chromium/src/third_party/libevent/include/event2/util.h
@@ -837,17 +837,17 @@ int evutil_secure_rng_init(void);
  * user of the secure RNG might be running. Don't pass anything other than a
  * real /dev/...random device file here, or you might lose security.)
  *
  * This API is unstable, and might change in a future libevent version.
  */
 EVENT2_EXPORT_SYMBOL
 int evutil_secure_rng_set_urandom_device_file(char *fname);
 
-#if !defined(__OpenBSD__) && !defined(ANDROID)
+#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(ANDROID)
 /** Seed the random number generator with extra random bytes.
 
     You should almost never need to call this function; it should be
     sufficient to invoke evutil_secure_rng_init(), or let Libevent take
     care of calling evutil_secure_rng_init() on its own.
 
     If you call this function as a _replacement_ for the regular
     entropy sources, then you need to be sure that your input
--- a/ipc/chromium/src/third_party/libevent/patches/openbsd-no-arc4random_addrandom.patch
+++ b/ipc/chromium/src/third_party/libevent/patches/openbsd-no-arc4random_addrandom.patch
@@ -5,17 +5,17 @@ diff --git a/ipc/chromium/src/third_part
  #endif /* } !EVENT__HAVE_ARC4RANDOM */
  
  void
  evutil_secure_rng_get_bytes(void *buf, size_t n)
  {
  	ev_arc4random_buf(buf, n);
  }
  
-+#if !defined(__OpenBSD__) && !defined(ANDROID)
++#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(ANDROID)
  void
  evutil_secure_rng_add_bytes(const char *buf, size_t n)
  {
  	arc4random_addrandom((unsigned char*)buf,
  	    n>(size_t)INT_MAX ? INT_MAX : (int)n);
  }
 +#endif
  
@@ -31,17 +31,17 @@ diff --git a/ipc/chromium/src/third_part
   * user of the secure RNG might be running. Don't pass anything other than a
   * real /dev/...random device file here, or you might lose security.)
   *
   * This API is unstable, and might change in a future libevent version.
   */
  EVENT2_EXPORT_SYMBOL
  int evutil_secure_rng_set_urandom_device_file(char *fname);
  
-+#if !defined(__OpenBSD__) && !defined(ANDROID)
++#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(ANDROID)
  /** Seed the random number generator with extra random bytes.
  
      You should almost never need to call this function; it should be
      sufficient to invoke evutil_secure_rng_init(), or let Libevent take
      care of calling evutil_secure_rng_init() on its own.
  
      If you call this function as a _replacement_ for the regular
      entropy sources, then you need to be sure that your input
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -10,16 +10,18 @@
 #define vm_TypeInference_inl_h
 
 #include "vm/TypeInference.h"
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/Casting.h"
 #include "mozilla/PodOperations.h"
 
+#include <utility> // for ::std::swap
+
 #include "builtin/Symbol.h"
 #include "gc/GC.h"
 #include "jit/BaselineJIT.h"
 #include "js/HeapAPI.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/JSFunction.h"
 #include "vm/NativeObject.h"
@@ -1089,16 +1091,111 @@ struct TypeHashSet
             if (KEY::getKey(values[pos]) == key) {
                 return values[pos];
             }
             pos = (pos + 1) & (capacity - 1);
         }
 
         return nullptr;
     }
+
+    template <class T, class U, class Key, typename Fun>
+    static void
+    MapEntries(U**& values, unsigned count, Fun f)
+    {
+        // No element.
+        if (count == 0) {
+            MOZ_RELEASE_ASSERT(!values);
+            return;
+        }
+
+        // When we have a single element it is stored in-place of the function
+        // array pointer.
+        if (count == 1) {
+            values = reinterpret_cast<U**>(f(reinterpret_cast<U*>(values)));
+            return;
+        }
+
+        // When we have SET_ARRAY_SIZE or fewer elements, the values is an
+        // unorderred array.
+        if (count <= SET_ARRAY_SIZE) {
+            for (unsigned i = 0; i < count; i++) {
+                values[i] = f(values[i]);
+            }
+            return;
+        }
+
+        // Simple functions to read and mutate the lowest bit of pointers.
+        auto lowBit = [](U* elem) -> bool {
+            return bool(reinterpret_cast<uintptr_t>(elem) & 1);
+        };
+        auto toggleLowBit = [](U* elem) -> U* {
+            return reinterpret_cast<U*>(reinterpret_cast<uintptr_t>(elem) ^ 1);
+        };
+
+        // This code applies the function f and relocates the values based on
+        // the new pointers.
+        //
+        // To avoid allocations, we reuse the same structure but distinguish the
+        // elements to be rellocated from the rellocated elements with the
+        // lowest bit.
+        unsigned capacity = Capacity(count);
+        MOZ_RELEASE_ASSERT(uintptr_t(values[-1]) == capacity);
+        unsigned found = 0;
+        for (unsigned i = 0; i < capacity; i++) {
+            if (!values[i]) {
+                continue;
+            }
+            MOZ_ASSERT(found <= count);
+            U* elem = f(values[i]);
+            values[i] = nullptr;
+            MOZ_ASSERT(!lowBit(elem));
+            values[found++] = toggleLowBit(elem);
+        }
+        MOZ_ASSERT(found == count);
+
+        // Follow the same rule as InsertTry, except that for each cell we
+        // identify empty cell content with:
+        //
+        //   nullptr    empty cell.
+        //   0b....0    inserted element.
+        //   0b....1    empty cell - element to be inserted.
+        unsigned mask = capacity - 1;
+        for (unsigned i = 0; i < count; i++) {
+            U* elem = values[i];
+            if (!lowBit(elem)) {
+                // If this is a newly inserted element, this implies that one of
+                // the previous objects was moved to this position.
+                continue;
+            }
+            values[i] = nullptr;
+            while (elem) {
+                MOZ_ASSERT(lowBit(elem));
+                elem = toggleLowBit(elem);
+                unsigned pos = HashKey<T,Key>(Key::getKey(elem)) & mask;
+                while (values[pos] != nullptr && !lowBit(values[pos])) {
+                    pos = (pos + 1) & mask;
+                }
+                // The replaced element is either a nullptr, which stops this
+                // loop, or an element to be inserted, which would be inserted
+                // by this loop.
+                std::swap(values[pos], elem);
+            }
+        }
+#ifdef DEBUG
+        unsigned inserted = 0;
+        for (unsigned i = 0; i < capacity; i++) {
+            if (!values[i]) {
+                continue;
+            }
+            inserted++;
+        }
+        MOZ_ASSERT(inserted == count);
+#endif
+    }
 };
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
 inline TypeSet::ObjectKey*
 TypeSet::Type::objectKey() const
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4541,57 +4541,23 @@ void
 ConstraintTypeSet::trace(Zone* zone, JSTracer* trc)
 {
     checkMagic();
 
     // ConstraintTypeSets only hold strong references during minor collections.
     MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
 
     unsigned objectCount = baseObjectCount();
-    if (objectCount >= 2) {
-        unsigned oldCapacity = TypeHashSet::Capacity(objectCount);
-        ObjectKey** oldArray = objectSet;
-
-        MOZ_RELEASE_ASSERT(uintptr_t(oldArray[-1]) == oldCapacity);
-
-        unsigned oldObjectCount = objectCount;
-        unsigned oldObjectsFound = 0;
-
-        clearObjects();
-        objectCount = 0;
-        for (unsigned i = 0; i < oldCapacity; i++) {
-            ObjectKey* key = oldArray[i];
-            if (!key) {
-                continue;
-            }
+    TypeHashSet::MapEntries<ObjectKey*, ObjectKey, ObjectKey>(
+        objectSet,
+        objectCount,
+        [&](ObjectKey* key) -> ObjectKey* {
             TraceObjectKey(trc, &key);
-            oldObjectsFound++;
-
-            AutoEnterOOMUnsafeRegion oomUnsafe;
-            ObjectKey** pentry =
-                TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
-                    (zone->types.typeLifoAlloc(), objectSet, objectCount, key);
-            if (!pentry) {
-                oomUnsafe.crash("ConstraintTypeSet::trace");
-            }
-
-            *pentry = key;
-        }
-        MOZ_RELEASE_ASSERT(oldObjectCount == oldObjectsFound);
-        setBaseObjectCount(objectCount);
-        // Note: -1/+1 to also poison the capacity field.
-        JS_POISON(oldArray - 1, JS_SWEPT_TI_PATTERN, (oldCapacity + 1) * sizeof(oldArray[0]),
-                  MemCheckKind::MakeUndefined);
-    } else if (objectCount == 1) {
-        ObjectKey* key = (ObjectKey*) objectSet;
-        TraceObjectKey(trc, &key);
-        objectSet = reinterpret_cast<ObjectKey**>(key);
-    } else {
-        MOZ_RELEASE_ASSERT(!objectSet);
-    }
+            return key;
+        });
 
 #ifdef DEBUG
     MOZ_ASSERT(objectCount == baseObjectCount());
     if (objectCount >= 2) {
         unsigned capacity = TypeHashSet::Capacity(objectCount);
         MOZ_ASSERT(uintptr_t(objectSet[-1]) == capacity);
         for (unsigned i = 0; i < capacity; i++) {
             ObjectKey* key = objectSet[i];
--- a/memory/replace/dmd/dmd.py
+++ b/memory/replace/dmd/dmd.py
@@ -327,25 +327,27 @@ def getDigestFromFile(args, inputFile):
 
     def buildTraceDescription(traceTable, frameTable, traceKey):
         frameKeys = traceTable[traceKey]
         fmt = '    #{:02d}{:}'
 
         if args.filter_stacks_for_testing:
             # When running SmokeDMD.cpp, every stack trace should contain at
             # least one frame that contains 'DMD.cpp', from either |DMD.cpp| or
-            # |SmokeDMD.cpp|. (Or 'dmd.cpp' on Windows.) If we see such a
+            # |SmokeDMD.cpp|. (Or 'dmd.cpp' on Windows.) On builds without
+            # debuginfo we expect just |SmokeDMD|. If we see such a
             # frame, we replace the entire stack trace with a single,
             # predictable frame. There is too much variation in the stack
             # traces across different machines and platforms to do more precise
             # matching, but this level of matching will result in failure if
             # stack fixing fails completely.
             for frameKey in frameKeys:
                 frameDesc = frameTable[frameKey]
-                if 'DMD.cpp' in frameDesc or 'dmd.cpp' in frameDesc:
+                expected = ('DMD.cpp', 'dmd.cpp', 'SmokeDMD')
+                if any(ex in frameDesc for ex in expected):
                     return [fmt.format(1, ': ... DMD.cpp ...')]
 
         # The frame number is always '#00' (see DMD.h for why), so we have to
         # replace that with the correct frame number.
         desc = []
         for n, frameKey in enumerate(traceTable[traceKey], start=1):
             desc.append(fmt.format(n, frameTable[frameKey][3:]))
         return desc
--- a/memory/replace/dmd/test/xpcshell.ini
+++ b/memory/replace/dmd/test/xpcshell.ini
@@ -26,9 +26,9 @@ support-files =
   script-diff-live2.json
   script-diff-live-expected.txt
   script-diff-dark-matter1.json
   script-diff-dark-matter2.json
   script-diff-dark-matter-expected.txt
 
 [test_dmd.js]
 dmd = true
-skip-if = !(os=='linux' || os=='win' || os=='mac')
+skip-if = !(os=='linux' || os=='mac' || (os=='win' && !pgo))
--- a/python/mozboot/mozboot/freebsd.py
+++ b/python/mozboot/mozboot/freebsd.py
@@ -13,30 +13,30 @@ class FreeBSDBootstrapper(BaseBootstrapp
         BaseBootstrapper.__init__(self, **kwargs)
         self.version = int(version.split('.')[0])
         self.flavor = flavor.lower()
 
         self.packages = [
             'autoconf213',
             'gmake',
             'gtar',
-            'npm',
             'pkgconf',
             'py%s%s-sqlite3' % sys.version_info[0:2],
             'python3',
             'rust',
             'watchman',
             'zip',
         ]
 
         self.browser_packages = [
             'dbus-glib',
             'gconf2',
             'gtk2',
             'gtk3',
+            'libXt',
             'mesa-dri',  # depends on llvm*
             'pulseaudio',
             'v4l_compat',
             'yasm',
         ]
 
         if not self.which('as'):
             self.packages.append('binutils')
@@ -62,12 +62,15 @@ class FreeBSDBootstrapper(BaseBootstrapp
         self.ensure_browser_packages(artifact_mode=True)
 
     def ensure_browser_packages(self, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         self.pkg_install(*self.browser_packages)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # Clang / llvm already installed as browser package
-        self.ensure_rust_package('cbindgen')
+        self.pkg_install('rust-cbindgen')
+
+    def ensure_node_packages(self, state_dir, checkout_root):
+        self.pkg_install('npm')
 
     def upgrade_mercurial(self, current):
         self.pkg_install('mercurial')
--- a/python/mozboot/mozboot/openbsd.py
+++ b/python/mozboot/mozboot/openbsd.py
@@ -10,17 +10,16 @@ from mozboot.base import BaseBootstrappe
 class OpenBSDBootstrapper(BaseBootstrapper):
     def __init__(self, version, **kwargs):
         BaseBootstrapper.__init__(self, **kwargs)
 
         self.packages = [
             'autoconf-2.13',
             'gmake',
             'gtar',
-            'node',
             'rust',
             'wget',
             'unzip',
             'zip',
         ]
 
         self.browser_packages = [
             'llvm',
@@ -44,9 +43,12 @@ class OpenBSDBootstrapper(BaseBootstrapp
 
     def ensure_browser_packages(self, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         # we use -z because there's no other way to say "any autoconf-2.13"
         self.run_as_root(['pkg_add', '-z'] + self.browser_packages)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # Clang / llvm already installed as browser package
-        self.ensure_rust_package('cbindgen')
+        self.run_as_root(['pkg_add', 'cbindgen'])
+
+    def ensure_node_packages(self, state_dir, checkout_root):
+        self.run_as_root(['pkg_add', 'node'])
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -274,44 +274,44 @@ SSLConnectionErrorPrefix2=An error occur
 
 certErrorIntro=%S uses an invalid security certificate.
 
 certErrorTrust_SelfSigned=The certificate is not trusted because it is self-signed.
 certErrorTrust_UnknownIssuer=The certificate is not trusted because the issuer certificate is unknown.
 certErrorTrust_UnknownIssuer2=The server might not be sending the appropriate intermediate certificates.
 certErrorTrust_UnknownIssuer3=An additional root certificate may need to be imported.
 certErrorTrust_UnknownIssuer4=Someone could be trying to impersonate the site and you should not continue.
-# LOCALIZATION NOTE (certErrorTrust_UnknownIssuer5): %1$S is replaced by the brand name, %2$S is replaced by host name.
-certErrorTrust_UnknownIssuer5=Websites prove their identity via security certificates. %1$S does not trust %2$S because its security certificate issuer is unknown, the certificate is self-signed, or the server is not sending the correct intermediate certificates.
+# LOCALIZATION NOTE (certErrorTrust_UnknownIssuer6): %1$S is replaced by the brand name, %2$S is replaced by host name.
+certErrorTrust_UnknownIssuer6=Websites prove their identity via certificates. %1$S does not trust %2$S because its certificate issuer is unknown, the certificate is self-signed, or the server is not sending the correct intermediate certificates.
 certErrorTrust_CaInvalid=The certificate is not trusted because it was issued by an invalid CA certificate.
 certErrorTrust_Issuer=The certificate is not trusted because the issuer certificate is not trusted.
 certErrorTrust_SignatureAlgorithmDisabled=The certificate is not trusted because it was signed using a signature algorithm that was disabled because that algorithm is not secure.
 certErrorTrust_ExpiredIssuer=The certificate is not trusted because the issuer certificate has expired.
 certErrorTrust_Untrusted=The certificate does not come from a trusted source.
 certErrorTrust_MitM=Your connection is being intercepted by a TLS proxy. Uninstall it if possible or configure your device to trust its root certificate.
 # LOCALIZATION NOTE (certErrorTrust_Symantec): %S is replaced by the domain for which the certificate is valid
 certErrorTrust_Symantec=The security certificate for %S is not trustworthy because the issuing organization failed to follow security practices. Certificates issued by Symantec, including the Thawte, GeoTrust, and RapidSSL brands, are not considered safe.
 
 certErrorMismatch=The certificate is not valid for the name %S.
-# LOCALIZATION NOTE (certErrorMismatch1, certErrorMismatchSinglePrefix1, certErrorMismatchMultiple1): %1$S is replaced by the brand name, %2$S is replaced by host name.
-certErrorMismatch1=Websites prove their identity via security certificates. %1$S does not trust %2$S because it uses a security certificate that is not valid for %2$S.
+# LOCALIZATION NOTE (certErrorMismatch2, certErrorMismatchSinglePrefix2, certErrorMismatchMultiple2): %1$S is replaced by the brand name, %2$S is replaced by host name.
+certErrorMismatch2=Websites prove their identity via certificates. %1$S does not trust %2$S because it uses a certificate that is not valid for %2$S.
 # LOCALIZATION NOTE (certErrorMismatchSinglePrefix): %S is replaced by the domain for which the certificate is valid
 certErrorMismatchSinglePrefix=The certificate is only valid for %S.
-# LOCALIZATION NOTE (certErrorMismatchSinglePrefix1): %3$S is replaced by the domain for which the certificate is valid
-certErrorMismatchSinglePrefix1=Websites prove their identity via security certificates. %1$S does not trust %2$S because it uses a security certificate that is not valid for %2$S.
+# LOCALIZATION NOTE (certErrorMismatchSinglePrefix2): %3$S is replaced by the domain for which the certificate is valid
+certErrorMismatchSinglePrefix2=Websites prove their identity via certificates. %1$S does not trust %2$S because it uses a certificate that is not valid for %2$S.
 certErrorMismatchMultiple=The certificate is only valid for the following names:
-certErrorMismatchMultiple1=Websites prove their identity via security certificates. %1$S does not trust %2$S because it uses a security certificate that is not valid for %2$S. The certificate is only valid for the following names:
+certErrorMismatchMultiple2=Websites prove their identity via certificates. %1$S does not trust %2$S because it uses a certificate that is not valid for %2$S. The certificate is only valid for the following names:
 
 # LOCALIZATION NOTE (certErrorExpiredNow): Do not translate %1$S (date+time of expired certificate) or %2$S (current date+time)
 certErrorExpiredNow=The certificate expired on %1$S. The current time is %2$S.
-certErrorExpiredNow1=Websites prove their identity via security certificates, which are valid for a set time period. The security certificate for %S appears to be expired.
+certErrorExpiredNow2=Websites prove their identity via certificates, which are valid for a set time period. The certificate for %S appears to be expired.
 
 # LOCALIZATION NOTE (certErrorNotYetValidNow): Do not translate %1$S (date+time certificate will become valid) or %2$S (current date+time)
 certErrorNotYetValidNow=The certificate will not be valid until %1$S. The current time is %2$S.
-certErrorNotYetValidNow1=Websites prove their identity via security certificates, which are valid for a set time period. The security certificate for %S appears to be not yet valid.
+certErrorNotYetValidNow2=Websites prove their identity via certificates, which are valid for a set time period. The certificate for %S appears to be not yet valid.
 
 # LOCALIZATION NOTE (certErrorSymantecDistrustDescription): %S will be replaced by the domain for which the certificate is valid.
 certErrorSymantecDistrustDescription=Websites prove their identity via certificates, which are issued by certificate authorities. Most browsers will no longer trust Symantec, the certificate authority for %S.
 certErrorSymantecDistrustAdministrator=You may notify the website’s administrator about this problem.
 
 # LOCALIZATION NOTE (certErrorCodePrefix3): %S is replaced by the error code.
 certErrorCodePrefix3=Error code: %S
 
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -55,50 +55,16 @@ linux64-plain/opt:
     run-on-projects: [trunk]
     toolchains:
         - linux64-binutils
         - linux64-clang
         - linux64-rust
         - linux64-node
         - linux64-cbindgen
 
-linux64-dmd/opt:
-    description: "Linux64 DMD Opt"
-    index:
-        product: firefox
-        job-name: linux64-dmd-opt
-    treeherder:
-        platform: linux64-dmd/opt
-        symbol: Bdmd
-        tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
-    worker:
-        max-run-time: 3600
-    run:
-        using: mozharness
-        actions: [get-secrets build check-test]
-        config:
-            - builds/releng_base_firefox.py
-            - builds/releng_base_linux_64_builds.py
-        script: "mozharness/scripts/fx_desktop_build.py"
-        extra-config:
-            mozconfig_variant: 'opt-dmd'
-        secrets: true
-        tooltool-downloads: public
-        need-xvfb: true
-    run-on-projects: []
-    toolchains:
-        - linux64-binutils
-        - linux64-clang
-        - linux64-rust
-        - linux64-rust-size
-        - linux64-cbindgen
-        - linux64-sccache
-        - linux64-node
-
 linux64/pgo:
     description: "Linux64 PGO"
     index:
         product: firefox
         job-name: linux64-pgo
     attributes:
         enable-full-crashsymbols: true
     treeherder:
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -104,54 +104,16 @@ macosx64-asan-fuzzing/opt:
         - linux64-libdmg
         - linux64-llvm-dsymutil
         - linux64-rust-macos
         - linux64-rust-size
         - linux64-cbindgen
         - linux64-sccache
         - linux64-node
 
-macosx64-dmd/opt:
-    description: "MacOS X x64 DMD Cross-compile"
-    index:
-        product: firefox
-        job-name: macosx64-dmd-opt
-    treeherder:
-        platform: osx-10-10-dmd/opt
-        symbol: Bdmd
-        tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
-    worker:
-        max-run-time: 3600
-        env:
-            TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
-    run:
-        using: mozharness
-        actions: [get-secrets build]
-        config:
-            - builds/releng_base_firefox.py
-            - builds/releng_base_mac_64_cross_builds.py
-        script: "mozharness/scripts/fx_desktop_build.py"
-        extra-config:
-            mozconfig_variant: 'opt-dmd'
-        secrets: true
-        tooltool-downloads: internal
-    run-on-projects: []
-    toolchains:
-        - linux64-cctools-port
-        - linux64-clang
-        - linux64-hfsplus
-        - linux64-libdmg
-        - linux64-llvm-dsymutil
-        - linux64-rust-macos
-        - linux64-rust-size
-        - linux64-cbindgen
-        - linux64-sccache
-        - linux64-node
-
 macosx64-devedition-nightly/opt:
     description: "MacOS X Dev Edition x64 Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: devedition
     index:
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -65,50 +65,16 @@ win32/opt:
     toolchains:
         - win64-clang-cl
         - win64-rust
         - win64-rust-size
         - win64-cbindgen
         - win64-sccache
         - win64-node
 
-win32-dmd/opt:
-    description: "Win32 DMD Opt"
-    index:
-        product: firefox
-        job-name: win32-dmd-opt
-    treeherder:
-        platform: windows2012-32-dmd/opt
-        symbol: Bdmd
-        tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
-    worker:
-        max-run-time: 7200
-        env:
-            TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
-    run:
-        using: mozharness
-        options: [append-env-variables-from-configs]
-        script: mozharness/scripts/fx_desktop_build.py
-        config:
-            - builds/releng_base_firefox.py
-            - builds/taskcluster_base_windows.py
-            - builds/taskcluster_base_win32.py
-            - builds/taskcluster_sub_win32/opt.py
-        extra-config:
-            mozconfig_variant: 'opt-dmd'
-    run-on-projects: []
-    toolchains:
-        - win64-clang-cl
-        - win64-rust
-        - win64-rust-size
-        - win64-cbindgen
-        - win64-sccache
-        - win64-node
-
 win32/pgo:
     description: "Win32 Opt PGO"
     index:
         product: firefox
         job-name: win32-pgo
     attributes:
         enable-full-crashsymbols: true
     treeherder:
@@ -260,50 +226,16 @@ win64-plain/opt:
             - builds/taskcluster_sub_win64/plain_opt.py
     run-on-projects: [trunk]
     toolchains:
         - win64-clang-cl
         - win64-rust
         - win64-node
         - win64-cbindgen
 
-win64-dmd/opt:
-    description: "Win64 DMD Opt"
-    index:
-        product: firefox
-        job-name: win64-dmd-opt
-    treeherder:
-        platform: windows2012-64-dmd/opt
-        symbol: Bdmd
-        tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
-    worker:
-        max-run-time: 7200
-        env:
-            TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
-    run:
-        using: mozharness
-        options: [append-env-variables-from-configs]
-        script: mozharness/scripts/fx_desktop_build.py
-        config:
-            - builds/releng_base_firefox.py
-            - builds/taskcluster_base_windows.py
-            - builds/taskcluster_base_win64.py
-            - builds/taskcluster_sub_win64/opt.py
-        extra-config:
-            mozconfig_variant: 'opt-dmd'
-    run-on-projects: []
-    toolchains:
-        - win64-clang-cl
-        - win64-rust
-        - win64-rust-size
-        - win64-cbindgen
-        - win64-sccache
-        - win64-node
-
 win32-nightly/opt:
     description: "Win32 Nightly"
     index:
         product: firefox
         job-name: win32-opt
         type: nightly
     attributes:
         nightly: true
--- a/taskcluster/ci/static-analysis/kind.yml
+++ b/taskcluster/ci/static-analysis/kind.yml
@@ -43,18 +43,18 @@ jobs:
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win32.py
                 - builds/taskcluster_sub_win32/clang_debug.py
         toolchains:
-            - win64-clang-cl-st-an
-            - win64-rust
+            - win32-clang-cl-st-an
+            - win32-rust
             - win64-cbindgen
             - win64-sccache
             - win64-node
 
     win32-st-an/opt:
         description: "Win32 Static Analysis Opt (clang-cl)"
         index:
             product: firefox
@@ -74,18 +74,18 @@ jobs:
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win32.py
                 - builds/taskcluster_sub_win32/clang.py
         toolchains:
-            - win64-clang-cl-st-an
-            - win64-rust
+            - win32-clang-cl-st-an
+            - win32-rust
             - win64-cbindgen
             - win64-sccache
             - win64-node
 
     win64-st-an/debug:
         description: "Win64 Static Analysis Debug (clang-cl)"
         index:
             product: firefox
--- a/taskcluster/ci/test/awsy.yml
+++ b/taskcluster/ci/test/awsy.yml
@@ -21,27 +21,44 @@ job-defaults:
 awsy:
     description: "Are we slim yet"
     treeherder-symbol: SY(sy)
     run-on-projects:
         by-test-platform:
             .*-devedition/.*: []  # don't run on devedition
             default: built-projects
 
+awsy-dmd:
+    description: "Are we slim yet - dmd enabled"
+    treeherder-symbol: SY(sy-d)
+    run-on-projects: ['try']
+    mozharness:
+        extra-options:
+            - --dmd
+
 awsy-base:
     description: "Are we slim yet - about:blank base case"
     treeherder-symbol: SY(ab)
     run-on-projects:
         by-test-platform:
             .*-devedition/.*: []  # don't run on devedition
             default: built-projects
     mozharness:
         extra-options:
             - --base
 
+awsy-base-dmd:
+    description: "Are we slim yet - about:blank base case - dmd enabled"
+    treeherder-symbol: SY(ab-d)
+    run-on-projects: ['try']
+    mozharness:
+        extra-options:
+            - --base
+            - --dmd
+
 awsy-stylo-sequential:
     description: "Are we slim yet for Stylo sequential"
     treeherder-symbol: SYss(sy)
     run-on-projects:
         by-test-platform:
             .*-devedition/.*: []  # don't run on devedition
             default: built-projects
     mozharness:
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -114,16 +114,18 @@ raptor-fetch-geckoview:
 raptor-fetch-chrome:
     - raptor-unity-webgl-chrome
     - raptor-wasm-misc-chrome
     - raptor-assorted-dom-chrome
 
 awsy:
     - awsy
     - awsy-base
+    - awsy-dmd
+    - awsy-base-dmd
 
 awsy-stylo-sequential:
     - awsy-stylo-sequential
 
 ##
 # Limited test sets for specific platforms
 
 linux-qr-tests:
--- a/taskcluster/ci/toolchain/windows.yml
+++ b/taskcluster/ci/toolchain/windows.yml
@@ -18,16 +18,37 @@ win64-clang-cl:
         using: toolchain-script
         script: build-clang64-windows.sh
         resources:
             - 'build/build-clang/build-clang.py'
             - 'build/build-clang/clang-win64.json'
             - 'taskcluster/scripts/misc/build-clang-windows-helper64.sh'
         toolchain-artifact: public/build/clang.tar.bz2
 
+win32-clang-cl-st-an:
+    description: "Clang-cl static analysis toolchain build"
+    treeherder:
+        kind: build
+        platform: toolchains/opt
+        symbol: TW32(clang-cl-st-an)
+        tier: 1
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        max-run-time: 7200
+        env:
+            TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/build-clang-cl.manifest"
+    run:
+        using: toolchain-script
+        script: build-clang32-st-an-windows.sh
+        resources:
+            - 'build/build-clang/build-clang.py'
+            - 'build/build-clang/clang-win32-st-an.json'
+            - 'taskcluster/scripts/misc/build-clang-windows-helper32.sh'
+        toolchain-artifact: public/build/clang.tar.bz2
+
 win64-clang-cl-st-an:
     description: "Clang-cl static analysis toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW64(clang-cl-st-an)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
@@ -183,16 +204,40 @@ win64-node:
         docker-image: {in-tree: toolchain-build}
         max-run-time: 1800
     run:
         using: toolchain-script
         script: repack-node.sh
         arguments: ['win64']
         toolchain-artifact: public/build/node.tar.bz2
 
+win32-rust-1.29:
+    description: "rust repack"
+    treeherder:
+        kind: build
+        platform: toolchains/opt
+        symbol: TW32(rust)
+        tier: 1
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        docker-image: {in-tree: toolchain-build}
+        max-run-time: 7200
+        env:
+            UPLOAD_DIR: artifacts
+    run:
+        using: toolchain-script
+        script: repack_rust.py
+        arguments: [
+            '--channel', '1.29.0',
+            '--host', 'i686-pc-windows-msvc',
+            '--target', 'i686-pc-windows-msvc',
+        ]
+        toolchain-alias: win32-rust
+        toolchain-artifact: public/build/rustc.tar.bz2
+
 mingw32-rust-1.29:
     description: "rust repack"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TMW(rust)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
new file mode 100755
--- /dev/null
+++ b/taskcluster/scripts/misc/build-clang32-st-an-windows.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source build/src/taskcluster/scripts/misc/build-clang-windows-helper32.sh clang-win32-st-an.json
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -548,26 +548,16 @@ def target_tasks_nightly_desktop(full_ta
         set(target_tasks_nightly_win32(full_task_graph, parameters, graph_config))
         | set(target_tasks_nightly_win64(full_task_graph, parameters, graph_config))
         | set(target_tasks_nightly_macosx(full_task_graph, parameters, graph_config))
         | set(target_tasks_nightly_linux(full_task_graph, parameters, graph_config))
         | set(target_tasks_nightly_asan(full_task_graph, parameters, graph_config))
     )
 
 
-# Opt DMD builds should only run nightly
-@_target_task('nightly_dmd')
-def target_tasks_dmd(full_task_graph, parameters, graph_config):
-    """Target DMD that run nightly on the m-c branch."""
-    def filter(task):
-        platform = task.attributes.get('build_platform', '')
-        return platform.endswith('-dmd')
-    return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
-
-
 # Run Searchfox analysis once daily.
 @_target_task('searchfox_index')
 def target_tasks_searchfox(full_task_graph, parameters, graph_config):
     """Select tasks required for indexing Firefox for Searchfox web site each day"""
     # For now we only do Linux and Mac debug builds. Windows builds
     # are currently broken (bug 1418415).
     return ['searchfox-linux64-searchfox/debug',
             'searchfox-macosx64-searchfox/debug']
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -389,17 +389,16 @@ class BuildOptionParser(object):
         'android-checkstyle': 'builds/releng_sub_%s_configs/%s_checkstyle.py',
         'android-lint': 'builds/releng_sub_%s_configs/%s_lint.py',
         'android-findbugs': 'builds/releng_sub_%s_configs/%s_findbugs.py',
         'android-geckoview-docs': 'builds/releng_sub_%s_configs/%s_geckoview_docs.py',
         'valgrind': 'builds/releng_sub_%s_configs/%s_valgrind.py',
         'artifact': 'builds/releng_sub_%s_configs/%s_artifact.py',
         'debug-artifact': 'builds/releng_sub_%s_configs/%s_debug_artifact.py',
         'devedition': 'builds/releng_sub_%s_configs/%s_devedition.py',
-        'dmd': 'builds/releng_sub_%s_configs/%s_dmd.py',
         'tup': 'builds/releng_sub_%s_configs/%s_tup.py',
     }
     build_pool_cfg_file = 'builds/build_pool_specifics.py'
     branch_cfg_file = 'builds/branch_specifics.py'
 
     @classmethod
     def _query_pltfrm_and_bits(cls, target_option, options):
         """ determine platform and bits
--- a/testing/mozharness/scripts/awsy_script.py
+++ b/testing/mozharness/scripts/awsy_script.py
@@ -51,16 +51,22 @@ class AWSY(TestingMixin, MercurialScript
           "default": False,
           "help": "Tries to enable the WebRender compositor.",
           }],
         [["--base"],
          {"action": "store_true",
           "dest": "test_about_blank",
           "default": False,
           "help": "Runs the about:blank base case memory test.",
+          }],
+        [["--dmd"],
+         {"action": "store_true",
+          "dest": "dmd",
+          "default": False,
+          "help": "Runs tests with DMD enabled.",
           }]
     ] + testing_config_options + copy.deepcopy(code_coverage_config_options)
 
     error_list = [
         {'regex': re.compile(r'''(TEST-UNEXPECTED|PROCESS-CRASH)'''), 'level': ERROR},
     ]
 
     def __init__(self, **kwargs):
@@ -157,17 +163,17 @@ class AWSY(TestingMixin, MercurialScript
         dmd_py_lib_dir = os.path.dirname(self.binary_path)
         if mozinfo.os == 'mac':
             # On mac binary is in MacOS and dmd.py is in Resources, ie:
             #   Name.app/Contents/MacOS/libdmd.dylib
             #   Name.app/Contents/Resources/dmd.py
             dmd_py_lib_dir = os.path.join(dmd_py_lib_dir, "../Resources/")
 
         dmd_path = os.path.join(dmd_py_lib_dir, "dmd.py")
-        if os.path.isfile(dmd_path):
+        if self.config['dmd'] and os.path.isfile(dmd_path):
             dmd_enabled = True
             runtime_testvars['dmd'] = True
 
             # Allow the child process to import dmd.py
             python_path = os.environ.get('PYTHONPATH')
 
             if python_path:
                 os.environ['PYTHONPATH'] = "%s%s%s" % (python_path, os.pathsep, dmd_py_lib_dir)
--- a/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini
+++ b/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini
@@ -21,8 +21,14 @@
     expected: FAIL
 
   [recordsAvailable is false after onbackgroundfetchsuccess finishes execution.]
     expected: FAIL
 
   [Using Background Fetch to fetch a non-existent resource should fail.]
     expected: FAIL
 
+  [Empty URL is OK.]
+    expected: FAIL
+
+  [Fetches with mixed content should fail.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/generic/only-valid-whitespaces-are-allowed.html.ini
@@ -0,0 +1,14 @@
+[only-valid-whitespaces-are-allowed.html]
+  expected: TIMEOUT
+  [U+00A0 NBSP  should not be parsed between directive name and value - HTTP header]
+    expected: TIMEOUT
+
+  [U+00A0 NBSP  should not be parsed inside directive value - meta tag]
+    expected: TIMEOUT
+
+  [U+00A0 NBSP  should not be parsed between directive name and value - meta tag]
+    expected: TIMEOUT
+
+  [U+00A0 NBSP  should not be parsed inside directive value - HTTP header]
+    expected: TIMEOUT
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-contain/contain-layout-button-001.html.ini
@@ -0,0 +1,2 @@
+[contain-layout-button-001.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-contain/contain-layout-flexbox-001.html.ini
@@ -0,0 +1,2 @@
+[contain-layout-flexbox-001.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-contain/contain-layout-grid-001.html.ini
@@ -0,0 +1,2 @@
+[contain-layout-grid-001.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-contain/contain-size-button-001.html.ini
@@ -0,0 +1,2 @@
+[contain-size-button-001.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-contain/contain-size-flexbox-001.html.ini
@@ -0,0 +1,2 @@
+[contain-size-flexbox-001.html]
+  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-contain/contain-size-grid-001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[contain-size-grid-001.html]
-  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-masking/mask-svg-content/mask-text-001.svg.ini
@@ -0,0 +1,2 @@
+[mask-text-001.svg]
+  expected: FAIL
--- a/testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/images3/object-position-svg-002o.html.ini
+++ b/testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/images3/object-position-svg-002o.html.ini
@@ -1,2 +1,4 @@
 [object-position-svg-002o.html]
-  expected: FAIL
+  expected:
+    if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): PASS
+    FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/eventsource/format-field-id-2.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[format-field-id-2.htm]
-  expected: TIMEOUT
-  [EventSource: Last-Event-ID (2)]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/eventsource/format-field-id.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[format-field-id.htm]
-  expected: TIMEOUT
-  [EventSource: Last-Event-ID]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/fetch/api/headers/header-values.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[header-values.html]
-  expected: TIMEOUT
-  [XMLHttpRequest with all valid values]
-    expected: TIMEOUT
-
-  [fetch() with all valid values]
-    expected: TIMEOUT
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html.ini
@@ -0,0 +1,7 @@
+[iframe-nosrc.html]
+  [load event of iframe should not be fired after processing the element]
+    expected: FAIL
+
+  [iframe.contentDocument should not be changed]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm.ini
@@ -0,0 +1,7 @@
+[intrinsic_sizes.htm]
+  [default object size after src is removed]
+    expected: FAIL
+
+  [default object size after poster is removed]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/intersection-observer/bounding-box.html.ini
@@ -0,0 +1,4 @@
+[bounding-box.html]
+  [target.style.zoom = 2]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/intersection-observer/target-in-different-window.html.ini
@@ -0,0 +1,6 @@
+[target-in-different-window.html]
+  [IntersectionObserver with target in a different window.]
+    expected:
+      if debug and not webrender and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): PASS
+      FAIL
+
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,2 +1,2 @@
-local: 136bf6adaec23a93d578837f0d3e1ba3f8510077
-upstream: 16c953fc379c1409856b7c70efa4adb79e4be4b1
+local: 32b6721da0406d86ab171e45b924b17750375e1e
+upstream: ec2304907681c938015c21e79a8a8fa0ff01ecb9
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/orientation-event/ondeviceorientationabsolute.html.ini
@@ -0,0 +1,10 @@
+[ondeviceorientationabsolute.html]
+  [Provide an event handler IDL attribute named ondeviceorientationabsolute]
+    expected: FAIL
+
+  [The absolute property must be set to true.]
+    expected: FAIL
+
+  [The type of this event handler must be 'DeviceOrientationEvent']
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/payment-method-id/payment-request-ctor-pmi-handling.https.html.ini
@@ -0,0 +1,13 @@
+[payment-request-ctor-pmi-handling.https.html]
+  [Must not throw on syntactically valid standardized payment method identifiers, even if they are not supported]
+    expected: FAIL
+
+  [Must support valid standard URL PMIs]
+    expected: FAIL
+
+  [Must throw on syntactically invalid standardized payment method identifiers]
+    expected: FAIL
+
+  [Constructor MUST throw if given an invalid URL-based payment method identifier]
+    expected: FAIL
+
--- a/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini
+++ b/testing/web-platform/meta/service-workers/cache-storage/serviceworker/__dir__.ini
@@ -1,1 +1,2 @@
 prefs: [dom.serviceWorkers.enabled: true, dom.serviceWorkers.exemptFromPerDomainMax:true, dom.caches.enabled:true]
+lsan-allowed: [Alloc, CompareNetwork, Create, CreateInner, EntrySlotOrCreate, MakeUnique, NewChannelFromURIWithProxyFlagsInternal, NewPage, PLDHashTable::Add, Realloc, SharedMutex, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::ThrottledEventQueue::Create, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::WorkerCSPEventListener::Create, mozilla::dom::WorkerPrivate::WorkerPrivate, mozilla::dom::serviceWorkerScriptCache::, mozilla::net::HttpBaseChannel::HttpBaseChannel, mozilla::net::HttpChannelChild::HttpChannelChild, mozilla::net::nsHttpHandler::NewProxiedChannel2, nsTimer::nsTimer]
--- a/testing/web-platform/meta/service-workers/service-worker/__dir__.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/__dir__.ini
@@ -1,2 +1,2 @@
 prefs: [dom.serviceWorkers.enabled:true]
-lsan-allowed: [Alloc, Create, CreateInner, MakeUnique, Malloc, NewChannelFromURIWithProxyFlagsInternal, NewEmptyScopeData, NewPage, OrInsert, PLDHashTable::Add, Realloc, SharedMutex, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::ThrottledEventQueue::Create, mozilla::WeakPtr, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::WorkerPrivate::WorkerPrivate, mozilla::net::HttpBaseChannel::HttpBaseChannel, mozilla::net::HttpChannelChild::HttpChannelChild, mozilla::net::nsHttpHandler::NewProxiedChannel2, nsNodeSupportsWeakRefTearoff::GetWeakReference, nsPrefetchService::Preload, nsSegmentedBuffer::AppendNewSegment]
+lsan-allowed: [Alloc, Create, CreateInner, MakeUnique, Malloc, NewChannelFromURIWithProxyFlagsInternal, NewEmptyScopeData, NewPage, OrInsert, PLDHashTable::Add, Realloc, SharedMutex, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::ThrottledEventQueue::Create, mozilla::WeakPtr, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::WorkerPrivate::WorkerPrivate, mozilla::net::HttpBaseChannel::HttpBaseChannel, mozilla::net::HttpChannelChild::HttpChannelChild, mozilla::net::nsHttpAuthIdentity::Set, mozilla::net::nsHttpHandler::NewProxiedChannel2, nsNodeSupportsWeakRefTearoff::GetWeakReference, nsPrefetchService::Preload, nsSegmentedBuffer::AppendNewSegment]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/service-workers/service-worker/import-module-scripts.https.html.ini
@@ -0,0 +1,22 @@
+[import-module-scripts.https.html]
+  [Static import and then dynamic import.]
+    expected: FAIL
+
+  [Static import.]
+    expected: FAIL
+
+  [Nested static import.]
+    expected: FAIL
+
+  [Dynamic import.]
+    expected: FAIL
+
+  [eval(import()).]
+    expected: FAIL
+
+  [Dynamic import and then static import.]
+    expected: FAIL
+
+  [Nested dynamic import.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/service-workers/service-worker/navigation-redirect.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/navigation-redirect.https.html.ini
@@ -1,4 +1,114 @@
 [navigation-redirect.https.html]
   [Service Worker: Navigation redirection]
     expected: FAIL
 
+
+[navigation-redirect.https.html?client]
+  [Redirect to other-origin in-scope with opaque redirect response which is passed through Cache.]
+    expected: FAIL
+
+  [SW-fetched redirect to other-origin in-scope.]
+    expected: FAIL
+
+  [SW-generated redirect to same-origin other-scope.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to other-origin and back to same-origin.]
+    expected: FAIL
+
+  [Redirect to other-origin out-scope with opaque redirect response.]
+    expected: FAIL
+
+  [No location redirect response via Cache.]
+    expected: FAIL
+
+  [Normal redirect to same-origin scope.]
+    expected: FAIL
+
+  [SW-generated redirect to other-origin in-scope.]
+    expected: FAIL
+
+  [Normal redirect to same-origin scope with different hash fragments.]
+    expected: FAIL
+
+  [Redirect to same-origin same-scope with opaque redirect response.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to same-origin same-scope with different hash fragments.]
+    expected: FAIL
+
+  [Redirect to same-origin out-scope with opaque redirect response.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to same-origin other-scope.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to same-origin same-scope with a hash fragment.]
+    expected: FAIL
+
+  [SW-generated redirect to other-origin out-scope.]
+    expected: FAIL
+
+  [SW-generated redirect to same-origin out-scope with different hash fragments.]
+    expected: FAIL
+
+  [Redirect to same-origin out-scope with opaque redirect response which is passed through Cache.]
+    expected: FAIL
+
+  [SW-generated redirect to same-origin out-scope.]
+    expected: FAIL
+
+  [Redirect to other-origin out-scope with opaque redirect response which is passed through Cache.]
+    expected: FAIL
+
+  [No location redirect response.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to same-origin out-scope.]
+    expected: FAIL
+
+  [Normal redirect to same-origin scope with a hash fragment.]
+    expected: FAIL
+
+  [Normal redirect to other-origin scope.]
+    expected: FAIL
+
+  [Redirect to same-origin same-scope with opaque redirect response which is passed through Cache.]
+    expected: FAIL
+
+  [Redirect to same-origin other-scope with opaque redirect response.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to same-origin same-scope.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to other-origin out-scope.]
+    expected: FAIL
+
+  [SW-fallbacked redirect to other-origin in-scope.]
+    expected: FAIL
+
+  [SW-fetched redirect to same-origin same-scope.]
+    expected: FAIL
+
+  [Redirect to other-origin in-scope with opaque redirect response.]
+    expected: FAIL
+
+  [SW-generated redirect to same-origin same-scope.]
+    expected: FAIL
+
+  [SW-fetched redirect to same-origin other-scope.]
+    expected: FAIL
+
+  [SW-fetched redirect to same-origin out-scope.]
+    expected: FAIL
+
+  [SW-generated redirect to same-origin out-scope with a hash fragment.]
+    expected: FAIL
+
+  [SW-fetched redirect to other-origin out-scope.]
+    expected: FAIL
+
+  [Redirect to same-origin other-scope with opaque redirect response which is passed through Cache.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/speech-api/SpeechSynthesisErrorEvent-constructor.html.ini
@@ -0,0 +1,4 @@
+[SpeechSynthesisErrorEvent-constructor.html]
+  [SpeechSynthesisErrorEvent with eventInitDict having utterance and error]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/speech-api/SpeechSynthesisUtterance-basics.https.html.ini
@@ -0,0 +1,4 @@
+[SpeechSynthesisUtterance-basics.https.html]
+  [new SpeechSynthesisUtterance(undefined)]
+    expected: FAIL
+
--- a/testing/web-platform/meta/speech-api/idlharness.window.js.ini
+++ b/testing/web-platform/meta/speech-api/idlharness.window.js.ini
@@ -114,19 +114,16 @@
     expected: FAIL
 
   [SpeechRecognitionResultList interface: existence and properties of interface prototype object]
     expected: FAIL
 
   [SpeechRecognitionResultList interface: existence and properties of interface prototype object's "constructor" property]
     expected: FAIL
 
-  [SpeechSynthesisErrorEvent interface object length]
-    expected: FAIL
-
   [SpeechRecognitionResult interface object name]
     expected: FAIL
 
   [SpeechGrammar interface: new SpeechGrammar() must inherit property "src" with the proper type]
     expected: FAIL
 
   [SpeechGrammarList interface: new SpeechGrammarList() must inherit property "item(unsigned long)" with the proper type]
     expected: FAIL
@@ -150,19 +147,16 @@
     expected: FAIL
 
   [SpeechGrammarList interface: existence and properties of interface prototype object]
     expected: FAIL
 
   [SpeechRecognition interface: new SpeechRecognition() must inherit property "start()" with the proper type]
     expected: FAIL
 
-  [SpeechSynthesisEvent interface object length]
-    expected: FAIL
-
   [SpeechRecognitionEvent interface: existence and properties of interface prototype object's "constructor" property]
     expected: FAIL
 
   [SpeechGrammarList interface: new SpeechGrammarList() must inherit property "length" with the proper type]
     expected: FAIL
 
   [SpeechRecognitionError interface: attribute message]
     expected: FAIL
--- a/testing/web-platform/meta/webrtc/RTCQuicTransport.https.html.ini
+++ b/testing/web-platform/meta/webrtc/RTCQuicTransport.https.html.ini
@@ -12,8 +12,38 @@
     expected: FAIL
 
   [getLocalParameters() has fingerprints for all certificates passed in the constructor.]
     expected: FAIL
 
   [stop() changes state to 'closed'.]
     expected: FAIL
 
+  [start() with a non-started RTCIceTransport does not change state.]
+    expected: FAIL
+
+  [RTCIceTransport.stop() changes RTCQuicTransport.state to 'closed'.]
+    expected: FAIL
+
+  [start() throws if called after the RTCIceTransport has stopped.]
+    expected: FAIL
+
+  [Two RTCQuicTransports connect to each other.]
+    expected: FAIL
+
+  [start() throws if called after stop().]
+    expected: FAIL
+
+  [start() throws if called twice.]
+    expected: FAIL
+
+  [RTCQuicTransport constructor throws if passed an RTCIceTransport that already has an active RTCQuicTransport.]
+    expected: FAIL
+
+  [start() with a started RTCIceTransport changes state to 'connecting'.]
+    expected: FAIL
+
+  [start() with a non-started RTCIceTransport later changes state to 'connecting' once the RTCIceTransport.start() is called.]
+    expected: FAIL
+
+  [stop() fires a statechange event to 'closed' on the remote transport]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html.ini
@@ -0,0 +1,2 @@
+[align_center_position_gt_50.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html.ini
@@ -0,0 +1,2 @@
+[align_center_position_lt_50.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html.ini
@@ -0,0 +1,2 @@
+[align_center_position_lt_50_size_gt_maximum_size.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html.ini
@@ -0,0 +1,2 @@
+[align_center_wrapped.html]
+  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[align_middle_position_gt_50.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[align_middle_position_lt_50.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[align_middle_position_lt_50_size_gt_maximum_size.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[align_middle_wrapped.html]
-  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/basic.html.ini
@@ -0,0 +1,2 @@
+[basic.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html.ini
@@ -0,0 +1,2 @@
+[regionanchor_x_50_percent.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html.ini
@@ -0,0 +1,2 @@
+[regionanchor_y_50_percent.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html.ini
@@ -0,0 +1,2 @@
+[scroll_up.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html.ini
@@ -0,0 +1,2 @@
+[single_line_top_left.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html.ini
@@ -0,0 +1,2 @@
+[viewportanchor_x_50_percent.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html.ini
@@ -0,0 +1,2 @@
+[viewportanchor_y_50_percent.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html.ini
@@ -0,0 +1,2 @@
+[width_50_percent.html]
+  expected: TIMEOUT
--- a/testing/web-platform/tests/.taskcluster.yml
+++ b/testing/web-platform/tests/.taskcluster.yml
@@ -1,11 +1,11 @@
 version: 1
 policy:
-  pullRequests: collaborators
+  pullRequests: public
 tasks:
   $flattenDeep:
     - $if: tasks_for == "github-push"
       then:
         $map:
           $flatten:
             $match: {
               event.ref == "refs/heads/master": [{name: firefox, channel: nightly}, {name: chrome, channel: dev}],
--- a/testing/web-platform/tests/README.md
+++ b/testing/web-platform/tests/README.md
@@ -427,21 +427,21 @@ changes to tests in a given directory, f
 META.yml file. Anyone with expertise in the specification under test can
 approve a pull request.  In particular, if a test change has already
 been adequately reviewed "upstream" in another repository, it can be
 pushed here without any further review by supplying a link to the
 upstream review.
 
 Search filters to find things to review:
 
-* [Open PRs (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22)
-* [Reviewed but still open PRs (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+-label%3Amozilla%3Agecko-sync+-label%3Achromium-export+-label%3Awebkit-export+-label%3Aservo-export+review%3Aapproved+-label%3A%22do+not+merge+yet%22+-label%3A%22status%3Aneeds-spec-decision%22) (Merge? Something left to fix? Ping other reviewer?)
+* [Open PRs (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22+-label%3Avendor-imports)
+* [Reviewed but still open PRs (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+-label%3Amozilla%3Agecko-sync+-label%3Achromium-export+-label%3Awebkit-export+-label%3Aservo-export+-label%3Avendor-imports+review%3Aapproved+-label%3A%22do+not+merge+yet%22+-label%3A%22status%3Aneeds-spec-decision%22) (Merge? Something left to fix? Ping other reviewer?)
 * [Open PRs without reviewers](https://github.com/web-platform-tests/wpt/pulls?q=is%3Apr+is%3Aopen+label%3Astatus%3Aneeds-reviewers)
-* [Open PRs with label `infra` (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3Ainfra+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22)
-* [Open PRs with label `docs` (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3Adocs+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22)
+* [Open PRs with label `infra` (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3Ainfra+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22+-label%3Avendor-imports)
+* [Open PRs with label `docs` (excluding vendor exports)](https://github.com/web-platform-tests/wpt/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3Adocs+-label%3A%22mozilla%3Agecko-sync%22+-label%3A%22chromium-export%22+-label%3A%22webkit-export%22+-label%3A%22servo-export%22+-label%3Avendor-imports)
 
 Getting Involved
 ================
 
 If you wish to contribute actively, you're very welcome to join the
 public-test-infra@w3.org mailing list (low traffic) by
 [signing up to our mailing list](mailto:public-test-infra-request@w3.org?subject=subscribe).
 The mailing list is [archived][mailarchive].
--- a/testing/web-platform/tests/accname/name_test_case_659-manual.html
+++ b/testing/web-platform/tests/accname/name_test_case_659-manual.html
@@ -61,16 +61,16 @@
   </head>
   <body>
   <p>This test examines the ARIA properties for Name test case 659.</p>
     <style type="text/css">
     label:before { content: "foo"; }
     label:after { content: "baz"; }
   </style>
   <form>
-    <label for="test" title="bar"><input id="test" type="text" name="test" title="bar"></label>
+    <label for="test" title="bar"><input id="test" type="text" name="test" title="buz"></label>
   </form>
 
   <div id="manualMode"></div>
   <div id="log"></div>
   <div id="ATTAmessages"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/accname/name_test_case_660-manual.html
+++ b/testing/web-platform/tests/accname/name_test_case_660-manual.html
@@ -61,16 +61,16 @@
   </head>
   <body>
   <p>This test examines the ARIA properties for Name test case 660.</p>
     <style type="text/css">
     label:before { content: "foo"; }
     label:after { content: "baz"; }
   </style>
   <form>
-    <label for="test" title="bar"><input id="test" type="password" name="test" title="bar"></label>
+    <label for="test" title="bar"><input id="test" type="password" name="test" title="buz"></label>
   </form>
 
   <div id="manualMode"></div>
   <div id="log"></div>
   <div id="ATTAmessages"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/background-fetch/fetch.https.window.js
+++ b/testing/web-platform/tests/background-fetch/fetch.https.window.js
@@ -65,16 +65,31 @@ backgroundFetchTest(async (test, backgro
     backgroundFetch.fetch('my-id', 'resources/feature-name.txt?2')
   ]));
 
 }, 'IDs must be unique among active Background Fetch registrations');
 
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registrationId = uniqueId();
   const registration =
+    await backgroundFetch.fetch(registrationId, '');
+
+  assert_equals(registration.id, registrationId);
+
+  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
+
+  assert_equals('backgroundfetchsuccess', type);
+  assert_equals(eventRegistration.result, 'success');
+  assert_equals(eventRegistration.failureReason, '');
+
+}, 'Empty URL is OK.');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registrationId = uniqueId();
+  const registration =
     await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
 
   assert_equals(registration.id, registrationId);
   assert_equals(registration.uploadTotal, 0);
   assert_equals(registration.uploaded, 0);
   assert_equals(registration.downloadTotal, 0);
   assert_equals(registration.result, '');
   assert_equals(registration.failureReason, '');
@@ -185,9 +200,25 @@ backgroundFetchTest(async (test, backgro
   assert_true(results[0].url.includes('resources/missing-cat.txt'));
   assert_equals(results[0].status, 404);
   assert_equals(results[0].text, '');
 
   assert_equals(eventRegistration.id, registration.id);
   assert_equals(eventRegistration.result, 'failure');
   assert_equals(eventRegistration.failureReason, 'bad-status');
 
-}, 'Using Background Fetch to fetch a non-existent resource should fail.');
\ No newline at end of file
+}, 'Using Background Fetch to fetch a non-existent resource should fail.');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registration = await backgroundFetch.fetch(
+                         'my-id',
+                         ['https://example.com', 'http://example.com']);
+
+  const {type, eventRegistration, results} = await getMessageFromServiceWorker();
+
+  assert_equals('backgroundfetchfail', type);
+  assert_equals(eventRegistration.failureReason, 'fetch-error');
+
+  assert_equals(results.length, 2);
+  assert_true(results[0].url.includes('https://example.com'));
+  assert_equals(results[1].url, '');
+
+}, 'Fetches with mixed content should fail.');
--- a/testing/web-platform/tests/background-fetch/mixed-content-and-allowed-schemes.https.window.js
+++ b/testing/web-platform/tests/background-fetch/mixed-content-and-allowed-schemes.https.window.js
@@ -27,44 +27,16 @@ backgroundFetchTest((t, bgFetch) => {
 }, 'loopback IPv6 http: fetch should register ok');
 
 backgroundFetchTest((t, bgFetch) => {
   return bgFetch.fetch(uniqueId(), 'http://localhost');
 }, 'localhost http: fetch should register ok');
 
 backgroundFetchTest((t, bgFetch) => {
   return promise_rejects(t, new TypeError(),
-                         bgFetch.fetch(uniqueId(), 'http://example.com'));
-}, 'non-loopback http: fetch should reject');
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(t, new TypeError(),
-                         bgFetch.fetch(uniqueId(), 'http://192.0.2.0'));
-}, 'non-loopback IPv4 http: fetch should reject');
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(t, new TypeError(),
-                         bgFetch.fetch(uniqueId(), 'http://[2001:db8::1]'));
-}, 'non-loopback IPv6 http: fetch should reject');
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(t, new TypeError(),
-                         bgFetch.fetch(uniqueId(), ['https://example.com',
-                                                    'http://example.com']));
-}, 'https: and non-loopback http: fetch should reject');
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(t, new TypeError(),
-                         bgFetch.fetch(uniqueId(), ['http://example.com',
-                                                    'https://example.com']));
-}, 'non-loopback http: and https: fetch should reject');
-
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(t, new TypeError(),
                          bgFetch.fetch(uniqueId(), 'wss:127.0.0.1'));
 }, 'wss: fetch should reject');
 
 backgroundFetchTest((t, bgFetch) => {
   return promise_rejects(t, new TypeError(),
                          bgFetch.fetch(uniqueId(), 'file:///'));
 }, 'file: fetch should reject');
 
--- a/testing/web-platform/tests/background-fetch/service_workers/sw.js
+++ b/testing/web-platform/tests/background-fetch/service_workers/sw.js
@@ -22,10 +22,8 @@ function handleBackgroundFetchUpdateEven
         const registrationCopy = cloneRegistration(event.registration);
         sendMessageToDocument(
           { type: event.type, eventRegistration: registrationCopy, results })
       }));
 }
 
 self.addEventListener('backgroundfetchsuccess', handleBackgroundFetchUpdateEvent);
 self.addEventListener('backgroundfetchfail', handleBackgroundFetchUpdateEvent);
-
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/generic/only-valid-whitespaces-are-allowed.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+  <script>
+    var tests = [
+      // Make sure that csp works properly in normal situations
+      { "csp": "", "expected": true, "name": "Should load image without any CSP" },
+      { "csp": "img-src 'none';", "expected": false, "name": "Should not load image with 'none' CSP" },
+      // Ensure ASCII whitespaces are properly parsed
+      // ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE.
+
+      // between directive name and value
+      { "csp": "img-src\u0009'none';", "expected": false, "name": "U+0009 TAB   should be properly parsed between directive name and value" },
+      { "csp": "img-src\u000C'none';", "expected": false, "name": "U+000C FF    should be properly parsed between directive name and value" },
+      { "csp": "img-src\u000A'none';", "expected": false, "name": "U+000A LF    should be properly parsed between directive name and value" },
+      { "csp": "img-src\u000D'none';", "expected": false, "name": "U+000D CR    should be properly parsed between directive name and value" },
+      { "csp": "img-src\u0020'none';", "expected": false, "name": "U+0020 SPACE should be properly parsed between directive name and value" },
+
+      // inside directive value
+      { "csp": "img-src http://example.com\u0009http://example2.com;", "expected": false, "name": "U+0009 TAB   should be properly parsed inside directive value" },
+      { "csp": "img-src http://example.com\u000Chttp://example2.com;", "expected": false, "name": "U+000C FF    should be properly parsed inside directive value" },
+      { "csp": "img-src http://example.com\u000Ahttp://example2.com;", "expected": false, "name": "U+000A LF    should be properly parsed inside directive value" },
+      { "csp": "img-src http://example.com\u000Dhttp://example2.com;", "expected": false, "name": "U+000D CR    should be properly parsed inside directive value" },
+      { "csp": "img-src http://example.com\u0020http://example2.com;", "expected": false, "name": "U+0020 SPACE should be properly parsed inside directive value" },
+
+      // Ensure nbsp (U+00A0) is not considered a valid whitespace
+      // https://github.com/webcompat/web-bugs/issues/18902 has more details about why this particularly relevant
+      { "csp": "img-src\u00A0'none';", "expected": true, "name": "U+00A0 NBSP  should not be parsed between directive name and value" },
+      { "csp": "img-src http://example.com\u00A0http://example2.com;", "expected": true, "name": "U+00A0 NBSP  should not be parsed inside directive value" },
+    ];
+
+    tests.forEach(test => {
+      async_test(t => {
+        var url = "support/load_img_and_post_result_meta.sub.html?csp=" + encodeURIComponent(test.csp);
+        test_image_loads_as_expected(test, t, url);
+      }, test.name + " - meta tag");
+
+      // We can't test csp delivered in an HTTP header if we're testing CR/LF characters
+      if (test.csp.indexOf("\u000A") == -1 && test.csp.indexOf("\u000D") == -1) {
+        async_test(t => {
+          var url = "support/load_img_and_post_result_meta.sub.html?csp=" + encodeURIComponent(test.csp);
+          test_image_loads_as_expected(test, t, url);
+        }, test.name + " - HTTP header");
+      }
+    });
+
+    function test_image_loads_as_expected(test, t, url) {
+      var i = document.createElement('iframe');
+      i.src = url;
+      window.addEventListener('message', t.step_func(function(e) {
+        if (e.source != i.contentWindow) return;
+        if (test.expected) {
+          assert_equals(e.data, "img loaded");
+        } else {
+          assert_equals(e.data, "img not loaded");
+        }
+        t.done();
+      }));
+      document.body.appendChild(i);
+    }
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/generic/support/load_img_and_post_result_header.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+  <script>
+    var img = document.createElement("img");
+    img.src = "/content-security-policy/support/pass.png";
+    img.onload = function() { parent.postMessage('img loaded', '*'); }
+    img.onerror = function() { parent.postMessage('img not loaded', '*'); }
+    document.body.appendChild(img);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/generic/support/load_img_and_post_result_header.html.sub.headers
@@ -0,0 +1,1 @@
+Content-Security-Policy: {{GET[csp]}}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/generic/support/load_img_and_post_result_meta.sub.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+  <meta http-equiv="Content-Security-Policy" content="{{GET[csp]}}">
+</head>
+<body>
+  <script>
+    var img = document.createElement("img");
+    img.src = "/content-security-policy/support/pass.png";
+    img.onload = function() { parent.postMessage('img loaded', '*'); }
+    img.onerror = function() { parent.postMessage('img not loaded', '*'); }
+    document.body.appendChild(img);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-backgrounds/border-image-width-008-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>border-image with different widths</title>
+<style>
+    #ref {
+        width: 360px;
+        height: 240px;
+        border-style: solid;
+        border-width: 40px 40px 20px 0px;
+        border-image-source: url("support/border.png");
+        border-image-slice: 27;
+    }
+</style>
+<body>
+    <div id="ref"></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-backgrounds/border-image-width-008.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>border-image-width has the same effect as a border-width and the image is displayed even if border-width is zero</title>
+<link rel="match" href="border-image-width-008-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#propdef-border-image-width">
+<style>
+    #test {
+        width: 400px;
+        height: 300px;
+        border-style: solid;
+        /* Note: Chrome does not display an image if border-width is 0 */
+        border-width: 0px;
+        border-image-source: url("support/border.png");
+        border-image-width: 40px 40px 20px 0px;
+        border-image-slice: 27;
+    }
+</style>
+<body>
+    <div id="test"></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/contain-layout-button-001.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Layout containment on button</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+<link rel="match" href="reference/contain-layout-button-001-ref.html">
+<meta name=assert content="Layout containment does apply to buttons, thus their baseline is the same than if they don't have contents.">
+<style>
+button {
+  border: 5px solid green;
+  padding: 0;
+  contain: layout;
+  color: transparent;
+  width: 0;
+  height: 0;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<button>b</button>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/contain-layout-flexbox-001.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Layout containment on flexbox container</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+<link rel="match" href="reference/contain-layout-flexbox-001-ref.html">
+<meta name=assert content="Layout containment does apply to flexbox containers, thus their baseline is the same than if they don't have contents.">
+<style>
+div {
+  display: inline-flex;
+  border: 5px solid green;
+  contain: layout;
+  color: transparent;
+  width: 0;
+  height: 0;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<div>f</div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/contain-layout-grid-001.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Layout containment on grid container</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-layout">
+<link rel="match" href="reference/contain-layout-grid-001-ref.html">
+<meta name=assert content="Layout containment does apply to grid containers, thus their baseline is the same than if they don't have contents.">
+<style>
+div {
+  display: inline-grid;
+  border: 5px solid green;
+  contain: layout;
+  color: transparent;
+  width: 0;
+  height: 0;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<div>g</div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/contain-paint-clip-019.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<meta charset="UTF-8">
+
+<title>CSS Containment Test: 'contain: paint' and clipping prevents scrollbars</title>
+
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-contain-1/#containment-paint">
+<link rel="match" href="reference/contain-paint-clip-019-ref.html">
+
+<meta content="This test checks that the paint containment of an element clips contents.
+  It should also prevent layout overflow from being propagated to ancestors." name="assert">
+<meta content="" name="flags">
+
+<style>
+  #container {
+    contain: paint;
+    width: 100px;
+    height: 100px;
+  }
+  #green {
+    background-color: green;
+    width: 100px;
+    height: 100px;
+  }
+  #red {
+    background-color: red;
+    width: 100px;
+    height: 10000px;
+  }
+</style>
+
+<p>Test passes if there there is a green square. No red and no scrollbars should be visible.</p>
+
+<div id="container">
+  <div id="green"></div>
+  <div id="red"></div>
+</div>
--- a/testing/web-platform/tests/css/css-contain/contain-size-button-001.html
+++ b/testing/web-platform/tests/css/css-contain/contain-size-button-001.html
@@ -1,18 +1,19 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Size containment on button</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
 <link rel="match" href="reference/contain-size-button-001-ref.html">
-<meta name=assert content="Size containment does apply to buttons, thus their size and baseline is the same than if they don't have contents.">
+<meta name=assert content="Size containment does apply to buttons, thus their size is the same than if they don't have contents.">
 <style>
 button {
   border: 5px solid green;
   padding: 0;
   contain: size;
   color: transparent;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
 before<button>flex</button>after
--- a/testing/web-platform/tests/css/css-contain/contain-size-flexbox-001.html
+++ b/testing/web-platform/tests/css/css-contain/contain-size-flexbox-001.html
@@ -1,18 +1,19 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Size containment on flexbox container</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
 <link rel="match" href="reference/contain-size-flexbox-001-ref.html">
-<meta name=assert content="Size containment does apply to flexbox containers, thus their size and baseline is the same than if they don't have contents.">
+<meta name=assert content="Size containment does apply to flexbox containers, thus their size is the same than if they don't have contents.">
 <style>
 div {
   display: inline-flex;
   border: 5px solid green;
   contain: size;
   color: transparent;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
 before<div>flex</div>after
--- a/testing/web-platform/tests/css/css-contain/contain-size-grid-001.html
+++ b/testing/web-platform/tests/css/css-contain/contain-size-grid-001.html
@@ -1,18 +1,19 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Size containment on grid container</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
 <link rel="match" href="reference/contain-size-grid-001-ref.html">
-<meta name=assert content="Size containment does apply to grid containers, thus their size and baseline is the same than if they don't have contents.">
+<meta name=assert content="Size containment does apply to grid containers, thus their size is the same than if they don't have contents.">
 <style>
 div {
   display: inline-grid;
   border: 5px solid green;
   contain: size;
   color: transparent;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
 before<div>grid</div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-layout-button-001-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Reference file</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<style>
+button {
+  border: 5px solid green;
+  padding: 0;
+  color: transparent;
+  width: 0;
+  height: 0px;
+  /* Layout containment creates a stacking context, the following lines simuluate the same in the reference file. */
+  position: relative;
+  z-index: 1;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<button></button>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-layout-flexbox-001-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Reference file</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<style>
+div {
+  display: inline-flex;
+  border: 5px solid green;
+  /* Layout containment creates a stacking context, the following lines simuluate the same in the reference file. */
+  position: relative;
+  z-index: 1;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<div></div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-layout-grid-001-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Reference file</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<style>
+div {
+  display: inline-grid;
+  border: 5px solid green;
+  /* Layout containment creates a stacking context, the following lines simuluate the same in the reference file. */
+  position: relative;
+  z-index: 1;
+}
+</style>
+
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the bottom, and then the word "after".</p>
+before<div></div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-paint-clip-019-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<meta charset="UTF-8">
+
+<title>CSS Reference Test</title>
+
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+
+<style>
+  #green {
+    background-color: green;
+    width: 100px;
+    height: 100px;
+  }
+</style>
+
+<p>Test passes if there there is a green square. No red and no scrollbars should be visible.</p>
+
+<div id="green"></div>
--- a/testing/web-platform/tests/css/css-contain/reference/contain-size-button-001-ref.html
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-size-button-001-ref.html
@@ -1,13 +1,17 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Reference file</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <style>
 button {
   border: 5px solid green;
   padding: 0;
+  color: transparent;
+  width: 0;
+  height: 0;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
-before<button></button>after
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
+before<button>b</button>after
--- a/testing/web-platform/tests/css/css-contain/reference/contain-size-flexbox-001-ref.html
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-size-flexbox-001-ref.html
@@ -1,13 +1,17 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Reference file</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <style>
 div {
   display: inline-flex;
   border: 5px solid green;
+  color: transparent;
+  width: 0;
+  height: 0;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
-before<div></div>after
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
+before<div>f</div>after
--- a/testing/web-platform/tests/css/css-contain/reference/contain-size-grid-001-ref.html
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-size-grid-001-ref.html
@@ -1,13 +1,17 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Containment Test: Reference file</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <style>
 div {
   display: inline-grid;
   border: 5px solid green;
+  color: transparent;
+  width: 0;
+  height: 0;
+  font-size: 2em;
 }
 </style>
 
-<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square, and then the word "after".</p>
-before<div></div>after
+<p>This test passes if it has the same output as the reference. You see the word "before", a 10px green square at the top, and then the word "after".</p>
+before<div>g</div>after
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-flexbox/flex-minimum-height-flex-items-009.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Tests correct handling of min-height: auto with dynamic changes</title>
+<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto" title="4.5. Implied Minimum Size of Flex Items" />
+<link rel="author" title="Google Inc." href="http://www.google.com/">
+<link href="support/flexbox.css" rel="stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<style>
+.container {
+  height: 300px;
+  outline: 2px solid black;
+}
+
+.inner
+{
+  width: 400px;
+  flex: 1;
+  background-color: green;
+}
+#container2 .flexbox > * { flex-basis: 0; }
+#container2 .column > * { flex-basis: auto; }
+</style>
+<script>
+function change() {
+  var container = document.getElementById('container');
+  container.offsetHeight;
+  container.style.height = '80px';
+  container = document.getElementById('container2');
+  container.offsetHeight;
+  container.style.height = '80px';
+  checkLayout('.container');
+}
+</script>
+<body onload="change()">
+<p>Green rectangle should be entirely within the black rectangle</p>
+<div id="log"></div>
+<div id="container" class="container">
+  <div class="flexbox column" style="height: 100%;">
+    <div class="flexbox flex-one">
+        <div class="flexbox column">
+          <div class="flexbox column flex-one">
+            <div class="inner" data-expected-height="80">
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
+
+<div id="container2" class="container">
+  <div class="flexbox column" style="height: 100%;">
+    <div class="flexbox flex-one">
+        <div class="flexbox column">
+          <div class="flexbox column flex-one">
+            <div class="inner" data-expected-height="80">
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/alignment/grid-gutters-013.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Gutters adjacent to collapsed tracks also collapse</title>
+<link rel="help" href="https://www.w3.org/TR/css-grid-1/#gutters">
+<link rel="help" href="https://www.w3.org/TR/css-align-3/#gap-shorthand">
+<link rel="help" href="https://www.w3.org/TR/css-align-3/#gap-legacy">
+<link rel="help" href="https://www.w3.org/TR/css-grid-1/#repeat-notation">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<meta name="assert" content="This test checks that gutters adjacent to collapsed tracks don't reduce the space available for aligning adjacent grid items." />
+<style>
+    #grid {
+        display: grid;
+        margin-top: -50px;
+        margin-left: -50px;
+        width: 500px;
+        height: 500px;
+        grid-gap: 100px;
+        grid-template-rows: repeat(auto-fit, 200px);
+        grid-template-columns: repeat(auto-fit, 200px);
+        align-items: center;
+        justify-items: center;
+        background: linear-gradient(red, red) no-repeat 50px 50px / 100px 100px;
+    }
+
+    #grid > div {
+        background-color: green;
+        width: 50%;
+        height: 50%;
+    }
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="grid">
+    <div></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/mask-negative-scale.svg
@@ -0,0 +1,40 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml"
+	 xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="testmeta">
+	<title>CSS Masking: mask with negative scale target</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-masks"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#MaskElement"/>
+	<html:link rel="match" href="reference/mask-negative-scale-001-ref.svg"/>
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">The masked target elements get scaled with negative
+	factors. Check if that influences masking. You should see 4 green
+	rectangles with smaller blue rectangles in it in various rotations.</desc>
+</g>
+<defs>
+<g id="img" transform="translate(10,10)">
+	<rect width="200" height="200" fill="red"/>
+	<rect width="100" height="100" fill="green"/>
+	<rect width="50" height="50" fill="blue"/>
+</g>
+</defs>
+
+<mask id="mask">
+	<rect x="10" y="10" width="90" height="90" fill="white"/>
+</mask>
+
+<g transform="translate(200, 200)">
+<g transform="matrix(1 0 0 1 -100 -100)" mask="url(#mask)">
+	<use xlink:href="#img"/>
+</g>
+<g transform="matrix(-1 0 0 -1 -100 -100)" mask="url(#mask)">
+	<use xlink:href="#img"/>
+</g>
+<g transform="matrix(-1 0 0 1 -100 -100)" mask="url(#mask)">
+	<use xlink:href="#img"/>
+</g>
+<g transform="matrix(1 0 0 -1 -100 -100)" mask="url(#mask)">
+	<use xlink:href="#img"/>
+</g>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/mask-text-001.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml"
+	 xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px">
+<g id="testmeta">
+	<title>CSS Masking: mask with transformed text content</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-masks"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#MaskElement"/>
+	<html:link rel="match" href="reference/mask-text-001-ref.svg"/>
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">The masked target elements get scaled with negative
+	factors. Check if that influences masking. You should see 4 green
+	rectangles with smaller blue rectangles in it in various rotations.</desc>
+</g>
+<mask id="mask">
+	<text fill="#fff" font-family="Ahem" font-size="12px" transform="rotate(90 50 50)" x="50%" y="50%">foobar</text>
+</mask>
+<rect width="100%" height="100%" x="0" y="0" mask="url(#mask)"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/mask-type-001.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: mask without mask-type alpha</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-masks"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#MaskElement"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#the-mask-type"/>
+	<html:link rel="match" href="reference/mask-green-square-001-ref.svg"/>
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">The mask type "alpha" is applied to the mask element.
+	The mask should take the alpha channel of the content to mask. You should
+	see a green square.</desc>
+</g>
+<mask id="mask" mask-type="alpha">
+	<rect width="200" height="200" fill="black" opacity="0"/>
+	<rect x="50" y="50" width="100" height="100" fill="black"/>
+</mask>
+<rect width="200" height="200" fill="green" mask="url(#mask)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/mask-type-002.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: mask without mask-type luminance</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-masks"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#MaskElement"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#the-mask-type"/>
+	<html:link rel="match" href="reference/mask-green-square-001-ref.svg"/>
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">The mask type "alpha" is applied to the mask element.
+	The mask should take the luminocity of the content to mask. You should
+	see a green square.</desc>
+</g>
+<mask id="mask" mask-type="luminance">
+	<rect width="200" height="200" fill="black"/>
+	<rect x="50" y="50" width="100" height="100" fill="white"/>
+</mask>
+<rect width="200" height="200" fill="green" mask="url(#mask)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/mask-type-003.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: mask without specified mask-type</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#svg-masks"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#MaskElement"/>
+	<html:link rel="help" href="http://www.w3.org/TR/css-masking-1/#the-mask-type"/>
+	<html:link rel="match" href="reference/mask-green-square-001-ref.svg"/>
+	<metadata class="flags">svg</metadata>
+	<desc class="assert">No mask type was specified the mask element should
+	take the luminocity of the content to mask. You should see a green square.
+	</desc>
+</g>
+<mask id="mask">
+	<rect width="200" height="200" fill="black"/>
+	<rect x="50" y="50" width="100" height="100" fill="white"/>
+</mask>
+<rect width="200" height="200" fill="green" mask="url(#mask)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/reference/mask-green-square-001-ref.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: Reftest reference</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<metadata class="flags">svg</metadata>
+</g>
+<rect x="50" y="50" width="100" height="100" fill="green"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/reference/mask-negative-scale-001-ref.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: Reftest reference</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<metadata class="flags">svg</metadata>
+</g>
+<rect width="90" height="90" fill="green"/>
+<rect x="40" y="40" width="50" height="50" fill="blue"/>
+<rect x="110" width="90" height="90" fill="green"/>
+<rect x="110" y="40" width="50" height="50" fill="blue"/>
+<rect y="110" width="90" height="90" fill="green"/>
+<rect x="40" y="110" width="50" height="50" fill="blue"/>
+<rect x="110" y="110" width="90" height="90" fill="green"/>
+<rect x="110" y="110" width="50" height="50" fill="blue"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-masking/mask-svg-content/reference/mask-text-001-ref.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+<g id="testmeta">
+	<title>CSS Masking: Reftest reference</title>
+	<html:link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com"/>
+	<metadata class="flags">svg</metadata>
+</g>
+<text fill="#000" font-family="Ahem" font-size="12px" transform="rotate(90 50 50)" x="50" y="50">foobar</text>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/white-space/reference/white-space-empty-text-sibling-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<p>There should be a space between "Two" and "words".</p>
+Two words
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/white-space/white-space-empty-text-sibling.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-rules">
+<link rel="match" href="reference/white-space-empty-text-sibling-ref.html">
+<p>There should be a space between "Two" and "words".</p>
+<div id="block"> <span>words</span></div>
+<script>
+  block.insertBefore(document.createTextNode(""), block.firstChild);
+  block.insertBefore(document.createTextNode(""), block.firstChild);
+  block.offsetTop;
+  block.firstChild.data = "Two";
+</script>
--- a/testing/web-platform/tests/css/motion/offset-path-serialization.html
+++ b/testing/web-platform/tests/css/motion/offset-path-serialization.html
@@ -31,12 +31,16 @@ test(function() {
 
   target.style.offsetPath = 'path("m 10.0 170.0 h 90.00 v 30.00 m 0 0 s 1 2 3 4 z c 9 8 7 6 5 4")';
   assert_equals(target.style.offsetPath, 'path("m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 Z c 9 8 7 6 5 4")');
   assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 170 H 100 V 200 M 100 200 S 101 202 103 204 Z C 109 208 107 206 105 204")');
 
   target.style.offsetPath = '  path(  "m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50"  )  ';
   assert_equals(target.style.offsetPath, 'path("m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50")');
   assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 20 A 10 20 30 1 0 50 70 A 110 120 30 1 1 190 120")');
+
+  target.style.offsetPath = 'path("M 1 2 H 3 v 4 h 5 V 6 h 7 v 8")';
+  assert_equals(target.style.offsetPath, 'path("M 1 2 H 3 v 4 h 5 V 6 h 7 v 8")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 1 2 H 3 V 6 H 8 V 6 H 15 V 14")');
 });
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-dyn-resize-001-ref.html
@@ -0,0 +1,60 @@
+<!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 Reftest Reference
+  </title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <style>
+    .container {
+      width: 100px;
+      display: flex;
+      border: 1px solid purple;
+      margin-bottom: 15px;
+    }
+    .item {
+       margin: 2px;
+       background: lightblue;
+    }
+    .inline-box {
+      display: inline-block;
+      height: 10px;
+      width: 10px;
+      background: lightgray;
+      border: 1px solid black;
+     }
+    #change-width {
+      /* Using hardcoded CSS as reference for testcase's tweak: */
+      width: 300px;
+    }
+    #change-flex {
+      /* Using hardcoded CSS as reference for testcase's tweak: */
+      flex: 0 0 75px;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="item" id="change-width">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+    <div class="item">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+  </div>
+
+  <div class="container">
+    <div class="item" id="change-flex">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+    <div class="item">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-dyn-resize-001.html
@@ -0,0 +1,65 @@
+<!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 a sizing change to one flex item impacts its sibling
+  </title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#layout-algorithm">
+  <link rel="match" href="flexbox-dyn-resize-001-ref.html">
+  <style>
+    .container {
+      width: 100px;
+      display: flex;
+      border: 1px solid purple;
+      margin-bottom: 15px;
+    }
+    .item {
+       margin: 2px;
+       background: lightblue;
+    }
+    .inline-box {
+      display: inline-block;
+      height: 10px;
+      width: 10px;
+      background: lightgray;
+      border: 1px solid black;
+     }
+  </style>
+  <script>
+  function go() {
+    // Make this item steal all the spare width (forcing its sibling to shrink)
+    // by giving it a huge 'width' and therefore huge flex-basis:
+    document.getElementById("change-width").style.width = "300px";
+
+    // Make this item steal all the spare width (forcing its sibling to shrink)
+    // by giving it a pretty big flex-basis and no shrinkability:
+    document.getElementById("change-flex").style.flex = "0 0 75px"
+  }
+  </script>
+</head>
+<body onload="go()">
+  <div class="container">
+    <div class="item" id="change-width">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+    <div class="item">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+  </div>
+
+  <div class="container">
+    <div class="item" id="change-flex">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+    <div class="item">
+      <div class="inline-box"></div><div class="inline-box"></div>
+    </div>
+  </div>
+</body>
+</html>
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list
+++ b/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list
@@ -103,16 +103,19 @@
 == flexbox-collapsed-item-horiz-003.html flexbox-collapsed-item-horiz-003-ref.html
 
 # Tests for "row gap" and "column gap"
 == flexbox-column-row-gap-001.html flexbox-column-row-gap-001-ref.html
 == flexbox-column-row-gap-002.html flexbox-column-row-gap-002-ref.html
 == flexbox-column-row-gap-003.html flexbox-column-row-gap-003-ref.html
 == flexbox-column-row-gap-004.html flexbox-column-row-gap-004-ref.html
 
+# Tests with dynamic changes that impact sizing:
+== flexbox-dyn-resize-001.html flexbox-dyn-resize-001-ref.html
+
 # Tests for "flex-basis: content"
 == flexbox-flex-basis-content-001a.html flexbox-flex-basis-content-001-ref.html
 == flexbox-flex-basis-content-001b.html flexbox-flex-basis-content-001-ref.html
 == flexbox-flex-basis-content-002a.html flexbox-flex-basis-content-002-ref.html
 == flexbox-flex-basis-content-002b.html flexbox-flex-basis-content-002-ref.html
 == flexbox-flex-basis-content-003a.html flexbox-flex-basis-content-003-ref.html
 == flexbox-flex-basis-content-003b.html flexbox-flex-basis-content-003-ref.html
 == flexbox-flex-basis-content-004a.html flexbox-flex-basis-content-004-ref.html
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/dom/events/shadow-relatedTarget.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+  This test is adopted from Olli Pettay's test case at
+  http://mozilla.pettay.fi/shadow_focus.html
+-->
+<div id="host"></div>
+<input id="lightInput">
+<script>
+const root = host.attachShadow({ mode: "closed" });
+root.innerHTML = "<input id='shadowInput'>";
+
+async_test((test) => {
+  root.getElementById("shadowInput").focus();
+  window.addEventListener("focus", test.step_func_done((e) => {
+    assert_equals(e.relatedTarget, host);
+  }, "relatedTarget should be pointing to shadow host."), true);
+  lightInput.focus();
+}, "relatedTarget should not leak at capturing phase, at window object.");
+
+async_test((test) => {
+  root.getElementById("shadowInput").focus();
+  lightInput.addEventListener("focus", test.step_func_done((e) => {
+    assert_equals(e.relatedTarget, host);
+  }, "relatedTarget should be pointing to shadow host."), true);
+  lightInput.focus();
+}, "relatedTarget should not leak at target.");
+
+</script>
--- a/testing/web-platform/tests/fullscreen/api/document-exit-fullscreen-timing-manual.html
+++ b/testing/web-platform/tests/fullscreen/api/document-exit-fullscreen-timing-manual.html
@@ -15,17 +15,17 @@ async_test(t => {
     document.exitFullscreen();
 
     // If fullscreenchange is an animation frame event, then animation frame
     // callbacks should be run after it is fired, before the timer callback.
     // The resize event should fire before the fullscreenchange event.
     const events = [];
     const callback = t.step_func(event => {
       // fullscreenElement should have changed before either event is fired.
-      assert_equals(document.fullscreenElement, null, `fullscreenElement in {event.type} event`);
+      assert_equals(document.fullscreenElement, null, `fullscreenElement in ${event.type} event`);
       events.push(event.type);
       if (event.type == 'fullscreenchange') {
         step_timeout(t.unreached_func('timer callback'));
         requestAnimationFrame(t.step_func_done(() => {
           assert_array_equals(events, ['resize', 'fullscreenchange'], 'event order');
         }));
       }
     });
--- a/testing/web-platform/tests/fullscreen/api/element-ready-check-containing-iframe-manual.html
+++ b/testing/web-platform/tests/fullscreen/api/element-ready-check-containing-iframe-manual.html
@@ -2,23 +2,24 @@
 <title>Element ready check for containing iframe</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../trusted-click.js"></script>
 <div id="log"></div>
 <iframe allowfullscreen></iframe>
 <iframe allowfullscreen></iframe>
 <script>
-async_test(function(t)
-{
-    var iframes = document.getElementsByTagName("iframe");
-    trusted_request(t, iframes[0].contentDocument.body, document.body);
-    iframes[0].contentDocument.onfullscreenchange = t.step_func(function()
-    {
-        assert_equals(document.fullscreenElement, iframes[0]);
-        trusted_request(t, iframes[1].contentDocument.body, iframes[0].contentDocument.body);
-        iframes[1].contentDocument.onfullscreenchange = t.step_func_done(function() {
-            assert_equals(document.fullscreenElement, iframes[1]);
-        });
-        iframes[1].contentDocument.onfullscreenerror = t.unreached_func("fullscreenchange error");
-    });
-});
+// wait for load event to avoid https://bugzil.la/1493878
+window.onload = function() {
+  async_test(function(t) {
+      var iframes = document.getElementsByTagName("iframe");
+      trusted_request(t, iframes[0].contentDocument.body, document.body);
+      iframes[0].contentDocument.onfullscreenchange = t.step_func(function() {
+          assert_equals(document.fullscreenElement, iframes[0]);
+          trusted_request(t, iframes[1].contentDocument.body, iframes[0].contentDocument.body);
+          iframes[1].contentDocument.onfullscreenchange = t.step_func_done(function() {
+              assert_equals(document.fullscreenElement, iframes[1]);
+          });
+          iframes[1].contentDocument.onfullscreenerror = t.unreached_func("fullscreenchange error");
+      });
+  });
+};
 </script>
--- a/testing/web-platform/tests/fullscreen/api/element-ready-check-not-allowed-manual.html
+++ b/testing/web-platform/tests/fullscreen/api/element-ready-check-not-allowed-manual.html
@@ -1,19 +1,21 @@
 <!DOCTYPE html>
 <title>Element ready check with enabled flag not set</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../trusted-click.js"></script>
 <div id="log"></div>
 <iframe></iframe>
 <script>
-async_test(function(t)
-{
-    var iframe = document.querySelector("iframe");
-    document.onfullscreenchange = t.unreached_func("document fullscreenchange event");
-    document.onfullscreenerror = t.unreached_func("document fullscreenerror event");
-    iframe.contentDocument.onfullscreenchange = t.unreached_func("iframe fullscreenchange event");
-    iframe.contentDocument.onfullscreenerror = t.step_func_done();
-    assert_false(iframe.contentDocument.fullscreenEnabled, "fullscreen enabled flag");
-    trusted_request(t, iframe.contentDocument.body, document.body);
-});
+// wait for load event to avoid https://bugzil.la/1493878
+window.onload = function() {
+  async_test(function(t) {
+      var iframe = document.querySelector("iframe");
+      document.onfullscreenchange = t.unreached_func("document fullscreenchange event");
+      document.onfullscreenerror = t.unreached_func("document fullscreenerror event");
+      iframe.contentDocument.onfullscreenchange = t.unreached_func("iframe fullscreenchange event");
+      iframe.contentDocument.onfullscreenerror = t.step_func_done();
+      assert_false(iframe.contentDocument.fullscreenEnabled, "fullscreen enabled flag");
+      trusted_request(t, iframe.contentDocument.body, document.body);
+  });
+};
 </script>
--- a/testing/web-platform/tests/fullscreen/api/element-request-fullscreen-and-exit-iframe-manual.html
+++ b/testing/web-platform/tests/fullscreen/api/element-request-fullscreen-and-exit-iframe-manual.html
@@ -1,42 +1,45 @@
 <!DOCTYPE html>
 <title>Element#requestFullscreen() and Document#exitFullscreen() in iframe</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../trusted-click.js"></script>
 <div id="log"></div>
 <iframe allowfullscreen></iframe>
 <script>
-async_test(t => {
-  const iframe = document.querySelector('iframe');
-  const iframeDoc = iframe.contentDocument;
-  const iframeBody = iframeDoc.body;
+// wait for load event to avoid https://bugzil.la/1493878
+window.onload = function() {
+  async_test(t => {
+    const iframe = document.querySelector('iframe');
+    const iframeDoc = iframe.contentDocument;
+    const iframeBody = iframeDoc.body;
 
-  let count = 0;
-  document.onfullscreenchange = iframeDoc.onfullscreenchange = t.step_func(event => {
-    count++;
-    assert_between_inclusive(count, 1, 4, 'number of fullscreenchange events');
-    // Both when entering and exiting, the fullscreenchange event is fired first
-    // on the outer document and then on the iframe's document. This is because
-    // the events are fired in animation frame tasks, which run in "frame tree"
-    // order.
-    const expected = {
-      target: count == 1 || count == 3 ? iframe : iframeBody,
-      outerFullscreenElement: count <= 2 ? iframe : null,
-      innerFullscreenElement: count <= 2 ? iframeBody : null,
-    };
-    assert_equals(event.target, expected.target, 'event target');
-    assert_equals(document.fullscreenElement, expected.outerFullscreenElement, 'outer fullscreenElement');
-    assert_equals(iframeDoc.fullscreenElement, expected.innerFullscreenElement, 'inner fullscreenElement');
-    if (count == 2) {
-      iframeDoc.exitFullscreen();
-    } else if (count == 4) {
-      // Done, but set timeout to fail on extra events.
-      step_timeout(t.step_func_done());
-    }
+    let count = 0;
+    document.onfullscreenchange = iframeDoc.onfullscreenchange = t.step_func(event => {
+      count++;
+      assert_between_inclusive(count, 1, 4, 'number of fullscreenchange events');
+      // Both when entering and exiting, the fullscreenchange event is fired first
+      // on the outer document and then on the iframe's document. This is because
+      // the events are fired in animation frame tasks, which run in "frame tree"
+      // order.
+      const expected = {
+        target: count == 1 || count == 3 ? iframe : iframeBody,
+        outerFullscreenElement: count <= 2 ? iframe : null,
+        innerFullscreenElement: count <= 2 ? iframeBody : null,
+      };
+      assert_equals(event.target, expected.target, 'event target');
+      assert_equals(document.fullscreenElement, expected.outerFullscreenElement, 'outer fullscreenElement');
+      assert_equals(iframeDoc.fullscreenElement, expected.innerFullscreenElement, 'inner fullscreenElement');
+      if (count == 2) {
+        iframeDoc.exitFullscreen();
+      } else if (count == 4) {
+        // Done, but set timeout to fail on extra events.
+        step_timeout(t.step_func_done());
+      }
+    });
+    document.onfullscreenerror = t.unreached_func('fullscreenerror event');
+    iframeDoc.onfullscreenerror = t.unreached_func('iframe fullscreenerror event');
+
+    trusted_request(t, iframeBody, iframeBody);
   });
-  document.onfullscreenerror = t.unreached_func('fullscreenerror event');
-  iframeDoc.onfullscreenerror = t.unreached_func('iframe fullscreenerror event');
-
-  trusted_request(t, iframeBody, iframeBody);
-});
+};
 </script>
--- a/testing/web-platform/tests/fullscreen/api/element-request-fullscreen-timing-manual.html
+++ b/testing/web-platform/tests/fullscreen/api/element-request-fullscreen-timing-manual.html
@@ -23,26 +23,30 @@ async_test(t => {
         assert_array_equals(events, ['resize', 'fullscreenchange'], 'event order');
       }));
     }
   });
   document.onfullscreenchange = window.onresize = callback;
 }, 'Timing of fullscreenchange and resize events');
 
 async_test(t => {
-  var promise = document.createElement('a').requestFullscreen();
-  var promise_executed = false;
-  if (promise) {
-    promise.catch(()=>{promise_executed = true; });
-  } else {
-    // if promises aren't supported treat it as executed.
-    promise_executed = true;
-  }
+  // Gecko throttles requestAnimationFrame before the first paint, so
+  // wrap the test to work around that.
+  requestAnimationFrame(t.step_func(() => {
+    var promise = document.createElement('a').requestFullscreen();
+    var promise_executed = false;
+    if (promise) {
+      promise.catch(()=>{promise_executed = true; });
+    } else {
+      // if promises aren't supported treat it as executed.
+      promise_executed = true;
+    }
 
-  // If fullscreenerror is an animation frame event, then animation frame
-  // callbacks should be run after it is fired, before the timer callback.
-  document.onfullscreenerror = t.step_func(() => {
-    assert_true(promise_executed, "promise executed");
-    step_timeout(t.unreached_func('timer callback'));
-    requestAnimationFrame(t.step_func_done());
-  });
+    // If fullscreenerror is an animation frame event, then animation frame
+    // callbacks should be run after it is fired, before the timer callback.
+    document.onfullscreenerror = t.step_func(() => {
+      assert_true(promise_executed, "promise executed");
+      step_timeout(t.unreached_func('timer callback'));
+      requestAnimationFrame(t.step_func_done());
+    });
+  }));
 }, 'Timing of fullscreenerror event');
 </script>
--- a/testing/web-platform/tests/fullscreen/model/move-to-fullscreen-iframe-manual.html
+++ b/testing/web-platform/tests/fullscreen/model/move-to-fullscreen-iframe-manual.html
@@ -1,37 +1,40 @@
 <!DOCTYPE html>
 <title>Moving fullscreen document's body into a fullscreen iframe</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="../trusted-click.js"></script>
 <iframe allowfullscreen></iframe>
 <script>
-async_test(t => {
-  const iframe = document.querySelector('iframe');
-  const iframeDoc = iframe.contentDocument;
+// wait for load event to avoid https://bugzil.la/1493878
+window.onload = function() {
+  async_test(t => {
+    const iframe = document.querySelector('iframe');
+    const iframeDoc = iframe.contentDocument;
 
-  // Enter fullscreen for the iframe's body element.
-  trusted_request(t, iframeDoc.body, iframeDoc.body);
-  document.onfullscreenchange = t.step_func(() => {
-    assert_equals(document.fullscreenElement, iframe, "document's initial fullscreen element");
-    assert_equals(iframeDoc.fullscreenElement, iframeDoc.body, "iframe's initial fullscreen element");
+    // Enter fullscreen for the iframe's body element.
+    trusted_request(t, iframeDoc.body, iframeDoc.body);
+    document.onfullscreenchange = t.step_func(() => {
+      assert_equals(document.fullscreenElement, iframe, "document's initial fullscreen element");
+      assert_equals(iframeDoc.fullscreenElement, iframeDoc.body, "iframe's initial fullscreen element");
 
-    // Then, move the outer document's body into the iframe. This is an unusual
-    // thing to do, but means that the iframe is removed from its document and
-    // should trigger fullscreen exit.
-    iframeDoc.documentElement.appendChild(document.body);
+      // Then, move the outer document's body into the iframe. This is an unusual
+      // thing to do, but means that the iframe is removed from its document and
+      // should trigger fullscreen exit.
+      iframeDoc.documentElement.appendChild(document.body);
+
+      // If we exit in an orderly fashion, that's all one can ask for.
+      document.onfullscreenchange = t.step_func_done(() => {
+        assert_equals(document.fullscreenElement, null, "document's final fullscreen element");
 
-    // If we exit in an orderly fashion, that's all one can ask for.
-    document.onfullscreenchange = t.step_func_done(() => {
-      assert_equals(document.fullscreenElement, null, "document's final fullscreen element");
-
-      // Because the iframe was removed, its browsing context was discarded and
-      // its contentDocument has become null. Because that browsing context was
-      // neither a descendant browsing context nor had an active document,
-      // nothing at all was done with it in the exit fullscreen algorithm, so
-      // its fullscreenElement is unchanged.
-      assert_equals(iframe.contentDocument, null, "iframe's content document");
-      assert_equals(iframeDoc.fullscreenElement, iframeDoc.body, "iframe's final fullscreen element");
+        // Because the iframe was removed, its browsing context was discarded and
+        // its contentDocument has become null. Because that browsing context was
+        // neither a descendant browsing context nor had an active document,
+        // nothing at all was done with it in the exit fullscreen algorithm, so
+        // its fullscreenElement is unchanged.
+        assert_equals(iframe.contentDocument, null, "iframe's content document");
+        assert_equals(iframeDoc.fullscreenElement, iframeDoc.body, "iframe's final fullscreen element");
+      });
     });
   });
-});
+};
 </script>
--- a/testing/web-platform/tests/fullscreen/rendering/ua-style-iframe-manual.html
+++ b/testing/web-platform/tests/fullscreen/rendering/ua-style-iframe-manual.html
@@ -9,27 +9,38 @@ iframe {
   padding: 1px;
   /* transform is also tested because of https://crbug.com/662393 */
   transform: scale(0.5);
 }
 </style>
 <div id="log"></div>
 <div id="ancestor"><iframe></iframe></div>
 <script>
+function assert_dir_properties(style, propBase, value, state) {
+  for (let dir of ["Top", "Right", "Bottom", "Left"]) {
+    let prop = propBase.replace('{}', dir);
+    assert_equals(style[prop], value, `${state} ${prop} style`);
+  }
+}
+
 async_test(t => {
   const ancestor = document.getElementById('ancestor');
   const iframe = ancestor.firstChild;
 
   const initialStyle = getComputedStyle(iframe);
-  assert_equals(initialStyle.border, '1px solid rgb(0, 0, 255)', 'initial border style');
-  assert_equals(initialStyle.padding, '1px', 'initial padding style');
+  assert_dir_properties(initialStyle, 'border{}Width', '1px', 'initial');
+  assert_dir_properties(initialStyle, 'border{}Style', 'solid', 'initial');
+  assert_dir_properties(initialStyle, 'border{}Color', 'rgb(0, 0, 255)', 'initial');
+  assert_dir_properties(initialStyle, 'padding{}', '1px', 'initial');
   assert_equals(initialStyle.transform, 'matrix(0.5, 0, 0, 0.5, 0, 0)', 'initial transform style');
 
   trusted_request(t, iframe);
 
   document.addEventListener('fullscreenchange', t.step_func_done(() => {
     const fullscreenStyle = getComputedStyle(iframe);
-    assert_equals(fullscreenStyle.border, '0px none rgb(0, 0, 0)', 'fullscreen border style');
-    assert_equals(fullscreenStyle.padding, '0px', 'fullscreen padding style');
+    assert_dir_properties(fullscreenStyle, 'border{}Width', '0px', 'fullscreen');
+    assert_dir_properties(fullscreenStyle, 'border{}Style', 'none', 'fullscreen');
+    assert_dir_properties(fullscreenStyle, 'border{}Color', 'rgb(0, 0, 0)', 'fullscreen');
+    assert_dir_properties(fullscreenStyle, 'padding{}', '0px', 'fullscreen');
     assert_equals(fullscreenStyle.transform, 'none', 'fullscreen transform style');
   }));
 });
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/form-margin-quirk.html
@@ -0,0 +1,20 @@
+<!-- quirks -->
+<title>form margin quirk</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+form { writing-mode: vertical-lr; }
+#ref { margin: 0 1em 0 0; }
+</style>
+<form id=form></form>
+<div id=ref></div>
+<script>
+test(() => {
+  const formStyle = getComputedStyle(document.getElementById('form'));
+  const refStyle = getComputedStyle(document.getElementById('ref'));
+  assert_equals(formStyle.marginTop, refStyle.marginTop, 'marginTop');
+  assert_equals(formStyle.marginRight, refStyle.marginRight, 'marginRight');
+  assert_equals(formStyle.marginBottom, refStyle.marginBottom, 'marginBottom');
+  assert_equals(formStyle.marginLeft, refStyle.marginLeft, 'marginLeft');
+});
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+table {
+  border-collapse: collapse;
+}
+td {
+  border: 5px solid black;
+  width: 100px;
+  height: 100px;
+}
+</style>
+Passes if there is a grid containing 2x2 squares.
+<table>
+  <tbody>
+    <tr><td></td><td></td></tr>
+    <tr><td></td><td></td></tr>
+  </tbody>
+</table>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/transformed-tbody-tr-collapsed-border.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Test for transformed tbody and tr with collapsed borders</title>
+<link rel="match" href="transformed-tbody-tr-collapsed-border-ref.html">
+<style>
+table {
+  border-collapse: collapse;
+}
+tbody, tr {
+  transform: translateY(0);
+}
+td {
+  border: 5px solid black;
+  width: 100px;
+  height: 100px;
+}
+</style>
+Passes if there is a grid containing 2x2 squares.
+<table>
+  <tbody>
+    <tr><td></td><td></td></tr>
+    <tr><td></td><td></td></tr>
+  </tbody>
+</table>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Check processing of iframe without src and srcdoc attribute</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+<iframe></iframe>
+<script>
+  let iframe = document.querySelector("iframe");
+
+  async_test(t => {
+    let originDoc = iframe.contentDocument;
+    window.addEventListener("load", t.step_func_done(() => {
+      assert_equals(iframe.contentDocument, originDoc, "contentDocument shouldn't be changed");
+    }));
+  }, "iframe.contentDocument should not be changed");
+
+  async_test(t => {
+    iframe.addEventListener("load", t.unreached_func());
+    window.addEventListener("load", () => t.done());
+  }, "load event of iframe should not be fired after processing the element");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm
@@ -0,0 +1,75 @@
+<!doctype html>
+<html>
+<head>
+<title>video element - intrinsic sizes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+</head>
+<body>
+<p><a href="https://html.spec.whatwg.org/multipage/#the-video-element">spec reference</a></p>
+<video id="v1"></video>
+<video id="v2" width="400"></video>
+<video id="v3" height="100"></video>
+<video id="v4"></video>
+<video id="v5" poster="/media/poster.png"></video>
+<div id="log"></div>
+<script>
+test(function() {
+  var s = getComputedStyle(document.getElementById("v1"));
+  assert_equals(s.width, "300px");
+  assert_equals(s.height, "150px");
+}, "default object size is 300x150");
+
+test(function() {
+  var s = getComputedStyle(document.getElementById("v2"));
+  assert_equals(s.width, "400px");
+  assert_equals(s.height, "200px");
+}, "default height is half the width");
+
+test(function() {
+  var s = getComputedStyle(document.getElementById("v3"));
+  assert_equals(s.width, "200px");
+  assert_equals(s.height, "100px");
+}, "default width is twice the height");
+
+async_test(function(t) {
+  var v = document.getElementById("v4");
+  var s = getComputedStyle(v);
+  v.src = getVideoURI("/media/movie_5") + "?" + new Date() + Math.random();
+  v.onerror = t.unreached_func();
+  v.onloadedmetadata = t.step_func(function() {
+    assert_equals(s.width, '320px');
+    assert_equals(s.height, '240px');
+    v.removeAttribute("src");
+    v.load();
+    // Dimensions should be updated only on next layout.
+    assert_equals(s.width, '320px');
+    assert_equals(s.height, '240px');
+    requestAnimationFrame(t.step_func_done(function() {
+      assert_equals(s.width, "300px");
+      assert_equals(s.height, "150px");
+    }));
+  });
+}, "default object size after src is removed");
+
+async_test(function(t) {
+  var v = document.getElementById("v5");
+  var s = getComputedStyle(v);
+  v.onerror = t.unreached_func();
+  onload = t.step_func(function() {
+    assert_equals(s.width, '102px');
+    assert_equals(s.height, '77px');
+    v.removeAttribute("poster");
+    // Dimensions should be updated only on next layout.
+    assert_equals(s.width, '102px');
+    assert_equals(s.height, '77px');
+    requestAnimationFrame(t.step_func_done(function() {
+      assert_equals(s.width, "300px");
+      assert_equals(s.height, "150px");
+    }));
+  });
+}, "default object size after poster is removed");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-legend-element/HTMLLegendElement.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: HTMLLegendElement</title>
+<link rel="author" title="Intel" href="http://www.intel.com/">
+<link rel="help" title="4.10.17 The legend element" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-legend-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div style="display:none">
+  <form>
+    <legend id="lgd1">test</legend>
+  </form>
+  <form id="fm">
+    <fieldset id="fs">
+      <legend id="lgd2">test</legend>
+    </fieldset>
+  </form>
+</div>
+<script>
+  test(function() {
+    assert_equals(document.getElementById("lgd1").form, null,
+                  "The legend.form return null if it has no fieldset parent.");
+  }, "The legend.form return null when it has no fieldset parent");
+
+  test(function() {
+    assert_equals(document.getElementById("lgd2").form, document.getElementById("fs").form,
+                  "The legend.form should be same as fieldset.form.");
+    assert_equals(document.getElementById("lgd2").form, document.getElementById("fm"),
+                  "The legend.form should be the correct form.");
+  }, "The legend.form must be same value as fieldset.form");
+
+  test(function() {
+    assert_true(document.getElementById("lgd1") instanceof HTMLLegendElement, "legend should be a HTMLLegendElement");
+    assert_readonly(document.getElementById("lgd1"), "form", "The form is not readonly");
+  }, "Interface HTMLLegendElement");
+</script>
+
--- a/testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html
+++ b/testing/web-platform/tests/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
 <title>HTML Templates: ownerDocument property of the element in template</title>
+<meta name="timeout" content="long">
 <meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
 <meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
 <meta name="assert" content="ownerDocument property of the element appended to template must be set to the template contents owner of the ownerDocument of the template element">
 <link rel="help" href="http://www.w3.org/TR/2013/WD-html-templates-20130214/#creating-an-element-for-a-token">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/html/resources/common.js"></script>
 </head>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled false</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<h2>Preconditions</h2>
+<p>Disable cookies in browser settings.</p>
+
+<script>
+  test(() => {
+    assert_false(navigator.cookieEnabled);
+  }, "navigator.cookieEnabled is false when cookies are disabled");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled true</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  test(() => {
+    assert_true(navigator.cookieEnabled);
+  }, "navigator.cookieEnabled is true when cookies are enabled");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/infrastructure/assumptions/cookie.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>cookies work in default browse settings</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-document-cookie">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  test(t => {
+    t.add_cleanup(() => {
+      let date = new Date();
+      date.setTime(date.getTime() - 10000);
+      document.cookie = "name=''; expires=" + date.toGMTString();
+    });
+    document.cookie = "name=test_cookie";
+    assert_not_equals(document.cookie.match(/name=test_cookie/), null);
+  });
+</script>
--- a/testing/web-platform/tests/interfaces/screen-capture.idl
+++ b/testing/web-platform/tests/interfaces/screen-capture.idl
@@ -2,22 +2,34 @@
 // Content was automatically extracted by Reffy into reffy-reports
 // (https://github.com/tidoust/reffy-reports)
 // Source: Screen Capture (https://w3c.github.io/mediacapture-screen-share/)
 
 partial interface Navigator {
     Promise<MediaStream> getDisplayMedia(optional MediaStreamConstraints constraints);
 };
 
+partial dictionary MediaTrackSupportedConstraints {
+             boolean displaySurface = true;
+             boolean logicalSurface = true;
+             boolean cursor = true;
+};
+
 partial dictionary MediaTrackConstraintSet {
              ConstrainDOMString displaySurface;
              ConstrainBoolean logicalSurface;
              ConstrainDOMString cursor;
 };
 
+partial dictionary MediaTrackSettings {
+             DOMString displaySurface;
+             boolean logicalSurface;
+             DOMString cursor;
+};
+
 enum DisplayCaptureSurfaceType {
     "monitor",
     "window",
     "application",
     "browser"
 };
 
 enum CursorCaptureConstraint {
--- a/testing/web-platform/tests/interfaces/speech-api.idl
+++ b/testing/web-platform/tests/interfaces/speech-api.idl
@@ -38,22 +38,28 @@ enum SpeechRecognitionErrorCode {
     "audio-capture",
     "network",
     "not-allowed",
     "service-not-allowed",
     "bad-grammar",
     "language-not-supported"
 };
 
-[Exposed=Window]
+[Exposed=Window,
+ Constructor(DOMString type, SpeechRecognitionErrorEventInit eventInitDict)]
 interface SpeechRecognitionErrorEvent : Event {
     readonly attribute SpeechRecognitionErrorCode error;
     readonly attribute DOMString message;
 };
 
+dictionary SpeechRecognitionErrorEventInit : EventInit {
+    required SpeechRecognitionErrorCode error;
+    DOMString message = "";
+};
+
 // Item in N-best list
 [Exposed=Window]
 interface SpeechRecognitionAlternative {
     readonly attribute DOMString transcript;
     readonly attribute float confidence;
 };
 
 // A complete one-shot simple response
@@ -67,24 +73,32 @@ interface SpeechRecognitionResult {
 // A collection of responses (used in continuous mode)
 [Exposed=Window]
 interface SpeechRecognitionResultList {
     readonly attribute unsigned long length;
     getter SpeechRecognitionResult item(unsigned long index);
 };
 
 // A full response, which could be interim or final, part of a continuous response or not
-[Exposed=Window]
+[Exposed=Window,
+ Constructor(DOMString type, SpeechRecognitionEventInit eventInitDict)]
 interface SpeechRecognitionEvent : Event {
     readonly attribute unsigned long resultIndex;
     readonly attribute SpeechRecognitionResultList results;
     readonly attribute any interpretation;
     readonly attribute Document? emma;
 };
 
+dictionary SpeechRecognitionEventInit : EventInit {
+    unsigned long resultIndex = 0;
+    required SpeechRecognitionResultList results;
+    any interpretation = null;
+    Document? emma = null;
+};
+
 // The object representing a speech grammar
 [Exposed=Window, Constructor]
 interface SpeechGrammar {
     attribute DOMString src;
     attribute float weight;
 };
 
 // The object representing a speech grammar collection
@@ -132,44 +146,57 @@ interface SpeechSynthesisUtterance : Eve
     attribute EventHandler onend;
     attribute EventHandler onerror;
     attribute EventHandler onpause;
     attribute EventHandler onresume;
     attribute EventHandler onmark;
     attribute EventHandler onboundary;
 };
 
-[Exposed=Window]
+[Exposed=Window,
+ Constructor(DOMString type, SpeechSynthesisEventInit eventInitDict)]
 interface SpeechSynthesisEvent : Event {
     readonly attribute SpeechSynthesisUtterance utterance;
     readonly attribute unsigned long charIndex;
     readonly attribute float elapsedTime;
     readonly attribute DOMString name;
 };
 
+dictionary SpeechSynthesisEventInit : EventInit {
+    required SpeechSynthesisUtterance utterance;
+    unsigned long charIndex = 0;
+    float elapsedTime = 0;
+    DOMString name = "";
+};
+
 enum SpeechSynthesisErrorCode {
     "canceled",
     "interrupted",
     "audio-busy",
     "audio-hardware",
     "network",
     "synthesis-unavailable",
     "synthesis-failed",
     "language-unavailable",
     "voice-unavailable",
     "text-too-long",
     "invalid-argument",
     "not-allowed",
 };
 
-[Exposed=Window]
+[Exposed=Window,
+ Constructor(DOMString type, SpeechSynthesisErrorEventInit eventInitDict)]
 interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
     readonly attribute SpeechSynthesisErrorCode error;
 };
 
+dictionary SpeechSynthesisErrorEventInit : SpeechSynthesisEventInit {
+    required SpeechSynthesisErrorCode error;
+};
+
 [Exposed=Window]
 interface SpeechSynthesisVoice {
     readonly attribute DOMString voiceURI;
     readonly attribute DOMString name;
     readonly attribute DOMString lang;
     readonly attribute boolean localService;
     readonly attribute boolean default;
 };
--- a/testing/web-platform/tests/intersection-observer/bounding-box.html
+++ b/testing/web-platform/tests/intersection-observer/bounding-box.html
@@ -8,17 +8,17 @@ pre, #log {
   position: absolute;
   top: 0;
   left: 200px;
 }
 #root {
   overflow: visible;
   height: 200px;
   width: 160px;
-  border: 7px solid black;
+  border: 8px solid black;
 }
 #target {
   margin: 10px;
   width: 100px;
   height: 100px;
   padding: 10px;
   background-color: green;
 }
@@ -45,17 +45,40 @@ runTestCycle(function() {
   assert_equals(entries.length, 0, "No initial notifications.");
   runTestCycle(step0, "First rAF.");
 }, "Test that the target's border bounding box is used to calculate intersection.");
 
 function step0() {
   var targetBounds = clientBounds(target);
   target.style.transform = "translateY(195px)";
   runTestCycle(step1, "target.style.transform = 'translateY(195px)'");
-  checkLastEntry(entries, 0, targetBounds.concat(0, 0, 0, 0, 8, 182, 8, 222, false));
+  checkLastEntry(entries, 0, targetBounds.concat(0, 0, 0, 0, 8, 184, 8, 224, false));
 }
 
 function step1() {
   var targetBounds = clientBounds(target);
+  target.style.transform = "translateY(300px)";
+  runTestCycle(step2, "target.style.transform = 'translateY(300px)'");
+  checkLastEntry(entries, 1, targetBounds.concat(26, 146, 221, 224, 8, 184, 8, 224, true));
+}
+
+function step2() {
+  var targetBounds = clientBounds(target);
   target.style.transform = "";
-  checkLastEntry(entries, 1, targetBounds.concat(25, 145, 220, 222, 8, 182, 8, 222, true));
+  target.style.zoom = "2";
+  runTestCycle(step3, "target.style.zoom = 2");
+  checkLastEntry(entries, 2, targetBounds.concat(0, 0, 0, 0, 8, 184, 8, 224, false));
 }
+
+function step3() {
+  var targetBounds = clientBounds(target);
+  var intersectionWidth = (
+      176  // root width including border
+      -8   // root left border
+      -20  // target left margin * target zoom
+  ) / 2;   // convert to target's zoom factor.
+  var intersectionHeight = (216 - 8 - 20) / 2;
+  var intersectionRect = [targetBounds[0], targetBounds[0] + intersectionWidth,
+                          targetBounds[2], targetBounds[2] + intersectionHeight];
+  checkLastEntry(entries, 3, targetBounds.concat(intersectionRect).concat(8, 184, 8, 224, true));
+}
+
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/intersection-observer/target-in-different-window.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/intersection-observer-test-utils.js"></script>
+
+<script>
+var entries = [];
+var popup, target;
+
+function waitForPopupNotification(f) {
+  popup.requestAnimationFrame(function() {
+    popup.requestAnimationFrame(function() { popup.setTimeout(f); });
+  });
+}
+
+async_test((t) => {
+  var observer = new IntersectionObserver(function(changes) {
+    entries = entries.concat(changes);
+  });
+  popup = window.open();
+  t.add_cleanup(() => popup.close());
+  target = popup.document.createElement('div');
+  target.style.width = "100px";
+  target.style.height = "100px";
+  observer.observe(target);
+  waitForPopupNotification(t.step_func(() => {
+    assert_equals(entries.length, 1, "Initial notification for detached target.");
+    assert_equals(entries[0].isIntersecting, false, "not intersecting");
+    popup.document.body.appendChild(target);
+    waitForPopupNotification(t.step_func_done(() => {
+      assert_equals(entries.length, 2, "Notification after insertion into popup.");
+      assert_equals(entries[1].isIntersecting, true, "intersecting");
+    }));
+  }));
+}, "IntersectionObserver with target in a different window.");
+</script>
--- a/testing/web-platform/tests/lint.whitelist
+++ b/testing/web-platform/tests/lint.whitelist
@@ -164,16 +164,17 @@ SET TIMEOUT: html/semantics/scripting-1/
 SET TIMEOUT: html/webappapis/dynamic-markup-insertion/opening-the-input-stream/0*
 SET TIMEOUT: html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/history-frame.html
 SET TIMEOUT: html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.js
 SET TIMEOUT: html/webappapis/scripting/event-loops/*
 SET TIMEOUT: html/webappapis/scripting/events/event-handler-processing-algorithm-error/*
 SET TIMEOUT: html/webappapis/scripting/processing-model-2/*
 SET TIMEOUT: IndexedDB/*
 SET TIMEOUT: infrastructure/*
+SET TIMEOUT: intersection-observer/target-in-different-window.html
 SET TIMEOUT: media-source/mediasource-util.js
 SET TIMEOUT: media-source/URL-createObjectURL-revoke.html
 SET TIMEOUT: mixed-content/generic/sanity-checker.js
 SET TIMEOUT: navigation-timing/*
 SET TIMEOUT: offscreen-canvas/the-offscreen-canvas/*
 SET TIMEOUT: old-tests/submission/Microsoft/history/history_000.htm
 SET TIMEOUT: paint-timing/resources/subframe-painting.html
 SET TIMEOUT: payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html
@@ -650,16 +651,18 @@ CSS-COLLIDING-REF-NAME: css/css-transfor
 CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/transforms/individual-transform-1-ref.html
 CSS-COLLIDING-REF-NAME: css/css-flexbox/reference/percentage-size-subitems-001-ref.html
 CSS-COLLIDING-REF-NAME: css/css-grid/grid-items/percentage-size-subitems-001-ref.html
 CSS-COLLIDING-REF-NAME: css/css-contain/reference/contain-size-button-001-ref.html
 CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-button-001-ref.html
 CSS-COLLIDING-REF-NAME: css/css-contain/reference/contain-size-grid-001-ref.html
 CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-grid-001-ref.html
 CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/support/red.png
+CSS-COLLIDING-REF-NAME: css/css-contain/reference/contain-size-fieldset-001-ref.html
+CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-fieldset-001-ref.html
 CSS-COLLIDING-SUPPORT-NAME: css/compositing/mix-blend-mode/support/red.png
 CSS-COLLIDING-SUPPORT-NAME: css/compositing/background-blending/support/red.png
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/normal-flow/support/replaced-min-max-3.png
 CSS-COLLIDING-SUPPORT-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/ui3/support/replaced-min-max-3.png
 CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/background-size/support/50x50-green.png
 CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/support/50x50-green.png
 CSS-COLLIDING-SUPPORT-NAME: css/css-grid/grid-items/support/50x50-green.png
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/support/50x50-green.png
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/navigation-timing/dom_interactive_image_document.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>Test domInteractive on image document.</title>
+        <link rel="author" title="Google" href="http://www.google.com/" />
+        <link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#read-media"/>
+        <script src="/resources/testharness.js"></script>
+        <script src="/resources/testharnessreport.js"></script>
+    </head>
+    <script>
+        const t = async_test("Test domInteractive on image document");
+        function frameLoaded() {
+            const timing = document.querySelector("iframe").contentWindow.performance.timing;
+            assert_greater_than(timing.domInteractive, 0,
+                "Expect domInteractive to be positive value.");
+            t.done();
+        }
+    </script>
+    <body>
+        <h1>Description</h1>
+        <p>This tests that a image document has positive-value domInteractive.</p>
+        <iframe src="../images/smiley.png" onload="frameLoaded()"></iframe>
+    </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/navigation-timing/dom_interactive_media_document.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>Test domInteractive on media document.</title>
+        <link rel="author" title="Google" href="http://www.google.com/" />
+        <link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#read-media"/>
+        <script src="/resources/testharness.js"></script>
+        <script src="/resources/testharnessreport.js"></script>
+    </head>
+    <script>
+        const t = async_test("Test domInteractive on media document");
+        function frameLoaded() {
+            const timing = document.querySelector("iframe").contentWindow.performance.timing;
+            assert_greater_than(timing.domInteractive, 0,
+                "Expect domInteractive to be positive value.");
+            t.done();
+        }
+    </script>
+    <body>
+        <h1>Description</h1>
+        <p>This tests that a media document has positive-value domInteractive.</p>
+        <iframe src="../media/A4.mp4" onload="frameLoaded()"></iframe>
+    </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/orientation-event/ondeviceorientationabsolute.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>User agents must also provide an event handler IDL attribute named ondeviceorientationabsolute on the window object</title>
+    <meta name=viewport content="width=device-width, maximum-scale=1.0">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <p>Rotate the device to run the tests.</p>
+    <div id="log"></div>
+    <script>
+      var t1 = async_test("Provide an event handler IDL attribute named ondeviceorientationabsolute");
+      var t2 = async_test("The type of this event handler must be 'DeviceOrientationEvent'");
+      var t3 = async_test("The absolute property must be set to true.");
+      var run = false;
+      if (window.ondeviceorientationabsolute === undefined) {
+        t1.step(t1.unreached_func("ondeviceorientationabsolute not supported"));
+        t2.step(t2.unreached_func("ondeviceorientationabsolute not supported"));
+        t3.step(t3.unreached_func("ondeviceorientationabsolute not supported"));
+      } else {
+        window.ondeviceorientationabsolute = function(e) {
+          if (!run) {
+            run = true;
+            t1.step(function() {
+              assert_equals(e.type, "deviceorientationabsolute");
+            });
+            t1.done();
+            t2.step(function() {
+              assert_true(e instanceof DeviceOrientationEvent);
+           });
+            t2.done();
+            t3.step(function() {
+              assert_true(e.absolute);
+            });
+            t3.done();
+          }
+        };
+      }
+    </script>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/payment-method-id/payment-request-ctor-pmi-handling.https.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Mozilla and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for validity of payment method identifiers during construction</title>
+<link rel="help" href="https://w3c.github.io/browser-payment-api/#constructor">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+const validAmount = Object.freeze({
+  currency: "USD",
+  value: "1.0",
+});
+const validTotal = Object.freeze({
+  label: "Default Total",
+  amount: validAmount,
+});
+const defaultDetails = Object.freeze({
+  total: validTotal,
+});
+
+test(() => {
+  const validMethods = [
+    "https://wpt",
+    "https://wpt.fyi/",
+    "https://wpt.fyi/payment",
+    "https://wpt.fyi/payment-request",
+    "https://wpt.fyi/payment-request?",
+    "https://wpt.fyi/payment-request?this=is",
+    "https://wpt.fyi/payment-request?this=is&totally",
+    "https://wpt.fyi:443/payment-request?this=is&totally",
+    "https://wpt.fyi:443/payment-request?this=is&totally#fine",
+    "https://:@wpt.fyi:443/payment-request?this=is&totally#👍",
+    " \thttps://wpt\n ",
+    "https://xn--c1yn36f",
+    "https://點看",
+  ];
+  for (const validMethod of validMethods) {
+    try {
+      const methods = [{ supportedMethods: validMethod }];
+      new PaymentRequest(methods, defaultDetails);
+    } catch (err) {
+      assert_unreached(
+        `Unexpected exception with valid standardized PMI: ${validMethod}. ${err}`
+      );
+    }
+  }
+}, "Must support valid standard URL PMIs");
+
+test(() => {
+  const validMethods = [
+    "e",
+    "n6jzof05mk2g4lhxr-u-q-w1-c-i-pa-ty-bdvs9-ho-ae7-p-md8-s-wq3-h-qd-e-q-sa",
+    "a-b-q-n-s-pw0",
+    "m-u",
+    "s-l5",
+    "k9-f",
+    "m-l",
+    "u4-n-t",
+    "i488jh6-g18-fck-yb-v7-i",
+    "x-x-t-t-c34-o",
+    "basic-card",
+    // gets coerced to "basic-card", for compat with old version of spec
+    ["basic-card"],
+  ];
+  for (const validMethod of validMethods) {
+    try {
+      const methods = [{ supportedMethods: validMethod }];
+      new PaymentRequest(methods, defaultDetails);
+    } catch (err) {
+      assert_unreached(
+        `Unexpected exception with valid standardized PMI: ${validMethod}. ${err}`
+      );
+    }
+  }
+}, "Must not throw on syntactically valid standardized payment method identifiers, even if they are not supported");
+
+test(() => {
+  const invalidMethods = [
+    "basic-💳",
+    "¡basic-*-card!",
+    "Basic-Card",
+    "0",
+    "-",
+    "--",
+    "a--b",
+    "-a--b",
+    "a-b-",
+    "0-",
+    "0-a",
+    "a0--",
+    "A-",
+    "A-B",
+    "A-b",
+    "a-0",
+    "a-0b",
+    " a-b",
+    "\t\na-b",
+    "a-b ",
+    "a-b\n\t",
+    "basic-card?not-really",
+    "basic-card://not-ok",
+    "basic card",
+    "/basic card/",
+    "BaSicCarD",
+    "BASIC-CARD",
+    " basic-card ",
+    "this is not supported",
+    " ",
+    "foo,var",
+    ["visa","mastercard"], // stringifies to "visa,mastercard"
+  ];
+  for (const invalidMethod of invalidMethods) {
+    assert_throws(
+      new RangeError(),
+      () => {
+        const methods = [{ supportedMethods: invalidMethod }];
+        new PaymentRequest(methods, defaultDetails);
+      },
+      `expected RangeError processing invalid standardized PMI "${invalidMethod}"`
+    );
+  }
+}, "Must throw on syntactically invalid standardized payment method identifiers");
+
+test(() => {
+  const invalidMethods = [
+    "https://username@example.com/pay",
+    "https://:password@example.com/pay",
+    "https://username:password@example.com/pay",
+    "http://username:password@example.com/pay",
+    "http://foo.com:100000000/pay",
+    "not-https://wpt.fyi/payment-request",
+    "../realitive/url",
+    "/absolute/../path?",
+    "https://",
+  ];
+  for (const invalidMethod of invalidMethods) {
+    assert_throws(
+      new RangeError(),
+      () => {
+        const methods = [{ supportedMethods: invalidMethod }];
+        new PaymentRequest(methods, defaultDetails);
+      },
+      `expected RangeError processing invalid URL PMI "${invalidMethod}"`
+    );
+  }
+}, "Constructor MUST throw if given an invalid URL-based payment method identifier");
+
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/import-module-scripts.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests for module import: ServiceWorker</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script src="/workers/modules/resources/import-test-cases.js"></script>
+<body>
+<script>
+function import_test(testCase) {
+  promise_test(async t => {
+    const msgPromise = new Promise(resolve => {
+      navigator.serviceWorker.onmessage = resolve;
+    });
+    await service_worker_unregister(t, testCase.scriptURL);
+    const registration = await navigator.serviceWorker.register(
+      testCase.scriptURL,
+      { scope: testCase.scriptURL, type: 'module' });
+    registration.installing.postMessage(
+      'Send message for tests from main script.');
+    const msgEvent = await msgPromise;
+    assert_array_equals(msgEvent.data, testCase.expectation);
+  }, testCase.description);
+}
+
+testCases.forEach(import_test);
+</script>
+</body>
--- a/testing/web-platform/tests/service-workers/service-worker/navigation-redirect.https.html
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-redirect.https.html
@@ -1,11 +1,16 @@
 <!DOCTYPE html>
 <title>Service Worker: Navigation redirection</title>
 <meta name="timeout" content="long">
+<!-- empty variant tests document.location and intercepted URLs -->
+<meta name="variant" content="">
+<!-- client variant tests the Clients API (resultingClientId and Client.url) -->
+<meta name="variant" content="?client">
+
 <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"></script>
 <body>
 <script>
 const host_info = get_host_info();
 
@@ -71,48 +76,216 @@ async function check_all_intercepted_url
   // OTHER_ORIGIN_IFRAME_URL resolves.
   const request_infos = await send_to_iframe(other_origin_frame,
                                              {command: 'get_request_infos'});
   urls.push(request_infos.map(info => { return info.url; }));
 
   assert_object_equals(urls, expected_urls, 'Intercepted URLs should match.');
 }
 
+// Checks |clients| returned from a worker. Only the client matching
+// |expected_final_client_tag| should be found. Returns true if a client was
+// found. Note that the final client is not necessarily found by this worker,
+// if the client is cross-origin.
+//
+// |clients| is an object like:
+// {x: {found: true, id: id1, url: url1}, b: {found: false}}
+function check_clients(clients,
+                       expected_id,
+                       expected_url,
+                       expected_final_client_tag,
+                       worker_name) {
+  let found = false;
+  Object.keys(clients).forEach(key => {
+    const info = clients[key];
+    if (info.found) {
+      assert_true(expected_final_client_tag,
+                  `${worker_name} client tag exists`);
+      assert_equals(key, expected_final_client_tag,
+                    `${worker_name} client tag matches`);
+      assert_equals(info.id, expected_id, `${worker_name} client id`);
+      assert_equals(info.url, expected_url, `${worker_name} client url`);
+      found = true;
+    }
+  });
+  return found;
+}
+
+function check_resulting_client_ids(infos, expected_infos, actual_ids, worker) {
+  assert_equals(infos.length, expected_infos.length,
+                `request length for ${worker}`);
+  for (var i = 0; i < infos.length; i++) {
+    const tag = expected_infos[i].resultingClientIdTag;
+    const url = expected_infos[i].url;
+    const actual_id = infos[i].resultingClientId;
+    const expected_id = actual_ids[tag];
+    assert_equals(typeof(actual_id), 'string',
+                  `resultingClientId for ${url} request to ${worker}`);
+    if (expected_id) {
+      assert_equals(requestInfos[0], expected_id,
+                    `resultingClientId for ${url} request to ${worker}`);
+    } else {
+      actual_ids[key] = actual_id;
+    }
+  }
+}
+
 // Creates an iframe and navigates to |url|, which is expected to start a chain
 // of redirects.
 // - |expected_last_url| is the expected window.location after the
 //   navigation.
+//
 // - |expected_request_infos| is the expected requests that the service workers
 //   were dispatched fetch events for. The format is:
 //   [
-//     [{url: url1}, {url: url2}], // requests from workers[0],
-//     [{url: url1},               // requests from workers[1],
-//     [{url: url1}, {url: url2}]  // requests from cross-origin worker
+//     [
+//       // Requests received by workers[0].
+//       {url: url1, resultingClientIdTag: 'a'},
+//       {url: url2, resultingClientIdTag: 'a'}
+//     ],
+//     [
+//       // Requests received by workers[1].
+//       {url: url3, resultingClientIdTag: 'a'}
+//     ],
+//     [
+//       // Requests received by the cross-origin worker.
+//       {url: url4, resultingClientIdTag: 'x'}
+//       {url: url5, resultingClientIdTag: 'x'}
+//     ]
 //   ]
+//   Here, |url| is |event.request.url| and |resultingClientIdTag| represents
+//   |event.resultingClientId|. Since the actual client ids are not known
+//   beforehand, the expectation isn't the literal expected value, but all equal
+//   tags must map to the same actual id.
+//
+// - |expected_final_client_tag| is the resultingClientIdTag that is
+//   expected to map to the created client's id. This is null if there
+//   is no such tag, which can happen when the final request was a cross-origin
+//   redirect to out-scope, so no worker received a fetch event whose
+//   resultingClientId is the id of the resulting client.
+//
+// In the example above:
+// - workers[0] receives two requests with the same resultingClientId.
+// - workers[1] receives one request also with that resultingClientId.
+// - The cross-origin worker receives two requests with the same
+//   resultingClientId which differs from the previous one.
+// - Assuming |expected_final_client_tag| is 'x', then the created
+//   client has the id seen by the cross-origin worker above.
 function redirect_test(url,
                        expected_last_url,
                        expected_request_infos,
+                       expected_final_client_tag,
                        test_name) {
   promise_test(async t => {
-    const message_promise = new Promise(resolve => {
-        // A message with ID 'last_url' will be sent from the iframe.
-        message_resolvers['last_url'] = resolve;
-    });
     const frame = await with_iframe(url);
     t.add_cleanup(() => { frame.remove(); });
 
-    const expected_intercepted_urls = expected_request_infos.map(requests => {
-      return requests.map(info => {
-        return info.url;
-      });
+    // Switch on variant.
+    if (document.location.search == '?client') {
+      return client_variant_test(url, expected_last_url, expected_request_infos,
+                                 expected_final_client_tag, test_name);
+    }
+
+    return default_variant_test(url, expected_last_url, expected_request_infos,
+                                frame, test_name);
+  }, test_name);
+}
+
+// The default variant tests the request interception chain and
+// resulting document.location.
+async function default_variant_test(url,
+                                    expected_last_url,
+                                    expected_request_infos,
+                                    frame,
+                                    test_name) {
+  const expected_intercepted_urls = expected_request_infos.map(
+      requests_for_worker => {
+    return requests_for_worker.map(info => {
+      return info.url;
     });
-    await check_all_intercepted_urls(expected_intercepted_urls);
-    const last_url = await message_promise;
-    assert_equals(last_url, expected_last_url, 'Last URL should match.');
-  }, test_name);
+  });
+  await check_all_intercepted_urls(expected_intercepted_urls);
+  const last_url = await send_to_iframe(frame, 'getLocation');
+  assert_equals(last_url, expected_last_url, 'Last URL should match.');
+}
+
+// The "client" variant tests the Clients API using resultingClientId.
+async function client_variant_test(url,
+                                   expected_last_url,
+                                   expected_request_infos,
+                                   expected_final_client_tag,
+                                   test_name) {
+  // Request infos is an array like:
+  // [
+  //   [{url: url1, resultingClientIdTag: tag1}],
+  //   [{url: url2, resultingClientIdTag: tag2}],
+  //   [{url: url3: resultingClientIdTag: tag3}]
+  // ]
+  const requestInfos = await get_all_request_infos();
+
+  // We check the actual infos against the expected ones, and learn the
+  // actual ids as we go.
+  const actual_ids = {};
+  check_resulting_client_ids(requestInfos[0],
+                             expected_request_infos[0],
+                             actual_ids,
+                             'worker0');
+  check_resulting_client_ids(requestInfos[1],
+                             expected_request_infos[1],
+                             actual_ids,
+                             'worker1');
+  check_resulting_client_ids(requestInfos[2],
+                             expected_request_infos[2],
+                             actual_ids,
+                             'crossOriginWorker');
+
+  // Now |actual_ids| maps tag to actual id:
+  // {x: id1, b: id2, c: id3}
+  // Ask each worker to try to resolve the actual ids to clients.
+  // Only |expected_final_client_tag| should resolve to a client.
+  const client_infos = await get_all_clients(actual_ids);
+
+  // Client infos is an object like:
+  // {
+  //   worker0: {x: {found: true, id: id1, url: url1}, b: {found: false}},
+  //   worker1: {x: {found: true, id: id1, url: url1}},
+  //   crossOriginWorker: {x: {found: false}}, {b: {found: false}}
+  // }
+  //
+  // Now check each client info. check_clients() verifies each info: only
+  // |expected_final_client_tag| should ever be found and the found client
+  // should have the expected url and id. A wrinkle is that not all workers
+  // will find the client, if they are cross-origin to the client. This
+  // means check_clients() trivially passes if no clients are found. So
+  // additionally check that at least one worker found the client (|found|),
+  // if that was expected (|expect_found|).
+  let found = false;
+  const expect_found = !!expected_final_client_tag;
+  const expected_id = actual_ids[expected_final_client_tag];
+  found = check_clients(client_infos.worker0,
+                        expected_id,
+                        expected_last_url,
+                        expected_final_client_tag,
+                        'worker0');
+  found = check_clients(client_infos.worker1,
+                        expected_id,
+                        expected_last_url,
+                        expected_final_client_tag,
+                        'worker1') || found;
+  found = check_clients(client_infos.crossOriginWorker,
+                        expected_id,
+                        expected_last_url,
+                        expected_final_client_tag,
+                        'crossOriginWorker') || found;
+  assert_equals(found, expect_found, 'client found');
+
+  if (!expect_found) {
+    // TODO(falken): Ask the other origin frame if it has a client of the
+    // expected URL.
+  }
 }
 
 window.addEventListener('message', on_message, false);
 
 function on_message(e) {
   if (e.origin != host_info['HTTPS_REMOTE_ORIGIN'] &&
       e.origin != host_info['HTTPS_ORIGIN'] ) {
     console.error('invalid origin: ' + e.origin);
@@ -124,364 +297,527 @@ function on_message(e) {
 }
 
 function send_to_iframe(frame, message) {
   var message_id = next_message_id++;
   return new Promise(resolve => {
     message_resolvers[message_id] = resolve;
     frame.contentWindow.postMessage(
         {id: message_id, message},
-        host_info['HTTPS_REMOTE_ORIGIN']);
+        '*');
+  });
+}
+
+async function get_all_clients(actual_ids) {
+  const client_infos = {};
+  client_infos['worker0'] = await get_clients(workers[0], actual_ids);
+  client_infos['worker1'] = await get_clients(workers[1], actual_ids);
+  client_infos['crossOriginWorker'] =
+      await send_to_iframe(other_origin_frame,
+                           {command: 'get_clients', actual_ids});
+  return client_infos;
+}
+
+function get_clients(worker, actual_ids) {
+  return new Promise(resolve => {
+    var channel = new MessageChannel();
+    channel.port1.onmessage = (msg) => {
+      resolve(msg.data.clients);
+    };
+    worker.postMessage({command: 'getClients', actual_ids, port: channel.port2},
+                       [channel.port2]);
   });
 }
 
 // Returns an array of the URLs that |worker| received fetch events for:
 //   [url1, url2]
 async function get_intercepted_urls(worker) {
   const infos = await get_request_infos(worker);
   return infos.map(info => { return info.url; });
 }
 
 // Returns the requests that |worker| received fetch events for. The return
 // value is an array of format:
-//   [{url: url1}, {url: url2}]
+// [
+//   {url: url1, resultingClientId: id},
+//   {url: url2, resultingClientId: id}
+// ]
 function get_request_infos(worker) {
   return new Promise(resolve => {
     var channel = new MessageChannel();
     channel.port1.onmessage = (msg) => {
       resolve(msg.data.requestInfos);
     };
     worker.postMessage({command: 'getRequestInfos', port: channel.port2},
                        [channel.port2]);
   });
 }
 
+// Returns an array of the requests the workers received fetch events for:
+// [
+//   // Requests from workers[0].
+//   [
+//     {url: url1, resultingClientIdTag: tag1},
+//     {url: url2, resultingClientIdTag: tag1}
+//   ],
+//
+//   // Requests from workers[1].
+//   [{url: url3, resultingClientIdTag: tag2}],
+//
+//   // Requests from the cross-origin worker.
+//   []
+// ]
+async function get_all_request_infos()  {
+  const request_infos = [];
+  request_infos.push(await get_request_infos(workers[0]));
+  request_infos.push(await get_request_infos(workers[1]));
+  request_infos.push(await send_to_iframe(other_origin_frame,
+                                          {command: 'get_request_infos'}));
+  return request_infos;
+}
+
 let url;
 let url1;
 let url2;
 
 // Normal redirect (from out-scope to in-scope).
 url = SCOPE1;
 redirect_test(
     OUT_SCOPE + 'url=' + encodeURIComponent(url),
     url,
-    [[{url}], [], []],
+    [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'Normal redirect to same-origin scope.');
 
 
 url = SCOPE1 + '#ref';
 redirect_test(
     OUT_SCOPE + 'url=' + encodeURIComponent(SCOPE1) + '#ref',
     url,
-    [[{url}], [], []],
+    [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'Normal redirect to same-origin scope with a hash fragment.');
 
 url = SCOPE1 + '#ref2';
 redirect_test(
     OUT_SCOPE + 'url=' + encodeURIComponent(url) + '#ref',
     url,
-    [[{url}], [], []],
+    [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'Normal redirect to same-origin scope with different hash fragments.');
 
 url = OTHER_ORIGIN_SCOPE;
 redirect_test(
     OUT_SCOPE + 'url=' + encodeURIComponent(url),
     url,
-    [[], [], [{url}]],
+    [[], [], [{url, resultingClientIdTag: 'x'}]],
+    'x',
     'Normal redirect to other-origin scope.');
 
-
 // SW fallbacked redirect. SW doesn't handle the fetch request.
 url = SCOPE1 + 'url=' + encodeURIComponent(OUT_SCOPE);
 redirect_test(
     url,
     OUT_SCOPE,
-    [[{url}], [], []],
+    [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'SW-fallbacked redirect to same-origin out-scope.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1);
 url2 = SCOPE1;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'SW-fallbacked redirect to same-origin same-scope.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1) + '#ref';
 url2 = SCOPE1 + '#ref';
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'SW-fallbacked redirect to same-origin same-scope with a hash fragment.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1 + '#ref2') + '#ref';
 url2 = SCOPE1 + '#ref2';
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'SW-fallbacked redirect to same-origin same-scope with different hash ' +
     'fragments.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE2);
 url2 = SCOPE2;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [{url: url2}], []],
+    [
+      [{url: url1, resultingClientIdTag: 'x'}],
+      [{url: url2, resultingClientIdTag: 'x'}],
+      []
+    ],
+    'x',
     'SW-fallbacked redirect to same-origin other-scope.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE);
 url2 = OTHER_ORIGIN_OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    'x',
     'SW-fallbacked redirect to other-origin out-scope.');
 
 url1 = SCOPE1 + 'url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE);
 url2 = OTHER_ORIGIN_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], [{url: url2}]],
+    [
+      [{url: url1, resultingClientIdTag: 'a'}],
+      [],
+      [{url: url2, resultingClientIdTag: 'x'}]
+    ],
+    'x',
     'SW-fallbacked redirect to other-origin in-scope.');
 
 
+url3 = SCOPE1;
+url2 = OTHER_ORIGIN_SCOPE + 'url=' + encodeURIComponent(url3);
+url1 = SCOPE1 + 'url=' + encodeURIComponent(url2);
+redirect_test(
+    url1,
+    url3,
+    [
+      [
+        {url: url1, resultingClientIdTag: 'a'},
+        {url: url3, resultingClientIdTag: 'x'}
+      ],
+      [],
+      [{url: url2, resultingClientIdTag: 'b'}]
+    ],
+    'x',
+    'SW-fallbacked redirect to other-origin and back to same-origin.');
+
 // SW generated redirect.
 // SW: event.respondWith(Response.redirect(params['url']));
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE);
 url2 = OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    null,
     'SW-generated redirect to same-origin out-scope.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE) + '#ref';
 url2 = OUT_SCOPE + '#ref';
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'SW-generated redirect to same-origin out-scope with a hash fragment.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE + '#ref2') + '#ref';
 url2 = OUT_SCOPE + '#ref2';
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'SW-generated redirect to same-origin out-scope with different hash ' +
     'fragments.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(SCOPE1);
 url2 = SCOPE1;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'SW-generated redirect to same-origin same-scope.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(SCOPE2);
 url2 = SCOPE2;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [{url: url2}], []],
+    [
+      [{url: url1, resultingClientIdTag: 'x'}],
+      [{url: url2, resultingClientIdTag: 'x'}],
+      []
+    ],
+    'x',
     'SW-generated redirect to same-origin other-scope.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE);
 url2 = OTHER_ORIGIN_OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    null,
     'SW-generated redirect to other-origin out-scope.');
 
 url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE);
 url2 = OTHER_ORIGIN_SCOPE;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}],
+      [{url: url1, resultingClientIdTag: 'a'}],
       [],
-      [{url: url2}]
+      [{url: url2, resultingClientIdTag: 'x'}]
     ],
+    'x',
     'SW-generated redirect to other-origin in-scope.');
 
 
 // SW fetched redirect.
 // SW: event.respondWith(fetch(event.request));
 url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OUT_SCOPE)
 url2 = OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'SW-fetched redirect to same-origin out-scope.');
 
 url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(SCOPE1);
 url2 = SCOPE1;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'SW-fetched redirect to same-origin same-scope.');
 
 url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(SCOPE2);
 url2 = SCOPE2;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}],
-      [{url: url2}],
+      [{url: url1, resultingClientIdTag: 'x'}],
+      [{url: url2, resultingClientIdTag: 'x'}],
       []
     ],
+    'x',
     'SW-fetched redirect to same-origin other-scope.');
 
 url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE);
 url2 = OTHER_ORIGIN_OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    null,
     'SW-fetched redirect to other-origin out-scope.');
 
 url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE);
 url2 = OTHER_ORIGIN_SCOPE;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}],
+      [{url: url1, resultingClientIdTag: 'a'}],
       [],
-      [{url: url2}]
+      [{url: url2, resultingClientIdTag: 'x'}]
     ],
+    'x',
     'SW-fetched redirect to other-origin in-scope.');
 
 
 // Opaque redirect.
 // SW: event.respondWith(fetch(
 //         new Request(event.request.url, {redirect: 'manual'})));
 url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OUT_SCOPE);
 url2 = OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'Redirect to same-origin out-scope with opaque redirect response.');
 
 url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(SCOPE1);
 url2 = SCOPE1;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}, {url: url2}], [], []],
+    [
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
+      [],
+      []
+    ],
+    'x',
     'Redirect to same-origin same-scope with opaque redirect response.');
 
 url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(SCOPE2);
 url2 = SCOPE2;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [{url: url2}], []],
+    [
+      [{url: url1, resultingClientIdTag: 'x'}],
+      [{url: url2, resultingClientIdTag: 'x'}],
+      []
+    ],
+    'x',
     'Redirect to same-origin other-scope with opaque redirect response.');
 
 url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE);
 url2 = OTHER_ORIGIN_OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    null,
     'Redirect to other-origin out-scope with opaque redirect response.');
 
 url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE);
 url2 = OTHER_ORIGIN_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], [{url: url2}]],
+    [
+      [{url: url1, resultingClientIdTag: 'a'}],
+      [],
+      [{url: url2, resultingClientIdTag: 'x'}]
+    ],
+    'x',
     'Redirect to other-origin in-scope with opaque redirect response.');
 
 url= SCOPE1 + 'sw=manual&noLocationRedirect';
 redirect_test(
-    url, url, [[{url}], [], []],
+    url, url, [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'No location redirect response.');
 
 
 // Opaque redirect passed through Cache.
 // SW responds with an opaque redirectresponse from the Cache API.
 url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(OUT_SCOPE);
 url2 = OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'Redirect to same-origin out-scope with opaque redirect response which ' +
     'is passed through Cache.');
 
 url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(SCOPE1);
 url2 = SCOPE1;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}, {url: url2}],
+      [
+        {url: url1, resultingClientIdTag: 'x'},
+        {url: url2, resultingClientIdTag: 'x'}
+      ],
       [],
       []
     ],
+    'x',
     'Redirect to same-origin same-scope with opaque redirect response which ' +
     'is passed through Cache.');
 
 url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(SCOPE2);
 url2 = SCOPE2;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}],
-      [{url: url2}],
+      [{url: url1, resultingClientIdTag: 'x'}],
+      [{url: url2, resultingClientIdTag: 'x'}],
       []
     ],
+    'x',
     'Redirect to same-origin other-scope with opaque redirect response which ' +
     'is passed through Cache.');
 
 url1 = SCOPE1 + 'sw=manualThroughCache&url=' +
        encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE);
 url2 = OTHER_ORIGIN_OUT_SCOPE;
 redirect_test(
     url1,
     url2,
-    [[{url: url1}], [], []],
+    [[{url: url1, resultingClientIdTag: 'a'}], [], []],
+    null,
     'Redirect to other-origin out-scope with opaque redirect response which ' +
     'is passed through Cache.');
 
 url1 = SCOPE1 + 'sw=manualThroughCache&url=' +
        encodeURIComponent(OTHER_ORIGIN_SCOPE);
 url2 = OTHER_ORIGIN_SCOPE;
 redirect_test(
     url1,
     url2,
     [
-      [{url: url1}],
+      [{url: url1, resultingClientIdTag: 'a'}],
       [],
-      [{url: url2}],
+      [{url: url2, resultingClientIdTag: 'x'}],
     ],
+    'x',
     'Redirect to other-origin in-scope with opaque redirect response which ' +
     'is passed through Cache.');
 
 url = SCOPE1 + 'sw=manualThroughCache&noLocationRedirect';
 redirect_test(
     url,
     url,
-    [[{url}], [], []],
+    [[{url, resultingClientIdTag: 'x'}], [], []],
+    'x',
     'No location redirect response via Cache.');
 
 // Clean up the test environment. This promise_test() needs to be the last one.
 promise_test(async t => {
   registrations.forEach(async registration => {
     if (registration)
       await registration.unregister();
   });
--- a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
@@ -39,31 +39,50 @@ function get_request_infos(worker) {
     channel.port1.onmessage = (msg) => {
       resolve(msg.data.requestInfos);
     };
     worker.postMessage({command: 'getRequestInfos', port: channel.port2},
                        [channel.port2]);
   });
 }
 
+function get_clients(worker, actual_ids) {
+  return new Promise(function(resolve) {
+      var channel = new MessageChannel();
+      channel.port1.onmessage = (msg) => {
+        resolve(msg.data.clients);
+      };
+      worker.postMessage({
+        command: 'getClients',
+        actual_ids,
+        port: channel.port2
+      }, [channel.port2]);
+    });
+}
+
 window.addEventListener('message', on_message, false);
 
 function on_message(e) {
   if (e.origin != host_info['HTTPS_ORIGIN']) {
     console.error('invalid origin: ' + e.origin);
     return;
   }
   const command = e.data.message.command;
   if (command == 'wait_for_worker') {
     wait_for_worker_promise.then(function() { send_result(e.data.id, 'ok'); });
   } else if (command == 'get_request_infos') {
     get_request_infos(worker)
       .then(function(data) {
           send_result(e.data.id, data);
         });
+  } else if (command == 'get_clients') {
+    get_clients(worker, e.data.message.actual_ids)
+      .then(function(data) {
+          send_result(e.data.id, data);
+        });
   } else if (command == 'unregister') {
     registration.unregister()
       .then(function() {
           send_result(e.data.id, 'ok');
         });
   }
 }
 
--- a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
@@ -6,15 +6,17 @@ def main(request, response):
     status = 200
 
     if "noLocationRedirect" in request.GET:
         status = 302
 
     return status, [("content-type", "text/html")], '''
 <!DOCTYPE html>
 <script>
+onmessage = event => {
   window.parent.postMessage(
       {
-        id: 'last_url',
+        id: event.data.id,
         result: location.href
       }, '*');
+};
 </script>
 '''
--- a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
@@ -6,15 +6,17 @@ def main(request, response):
     status = 200
 
     if "noLocationRedirect" in request.GET:
         status = 302
 
     return status, [("content-type", "text/html")], '''
 <!DOCTYPE html>
 <script>
+onmessage = event => {
   window.parent.postMessage(
       {
-        id: 'last_url',
+        id: event.data.id,
         result: location.href
       }, '*');
+};
 </script>
 '''
--- a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
@@ -6,15 +6,17 @@ def main(request, response):
     status = 200
 
     if "noLocationRedirect" in request.GET:
         status = 302
 
     return status, [("content-type", "text/html")], '''
 <!DOCTYPE html>
 <script>
+onmessage = event => {
   window.parent.postMessage(
       {
-        id: 'last_url',
+        id: event.data.id,
         result: location.href
       }, '*');
+};
 </script>
 '''
--- a/testing/web-platform/tests/service-workers/service-worker/resources/redirect-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/redirect-worker.js
@@ -1,41 +1,88 @@
 // We store an empty response for each fetch event request we see
 // in this Cache object so we can get the list of urls in the
 // message event.
 var cacheName = 'urls-' + self.registration.scope;
 
 var waitUntilPromiseList = [];
 
+// Sends the requests seen by this worker. The output is:
+// {
+//   requestInfos: [
+//     {url: url1, resultingClientId: id1},
+//     {url: url2, resultingClientId: id2},
+//   ]
+// }
 async function getRequestInfos(event) {
   // Wait for fetch events to finish.
   await Promise.all(waitUntilPromiseList);
   waitUntilPromiseList = [];
 
   // Generate the message.
   const cache = await caches.open(cacheName);
   const requestList = await cache.keys();
   const requestInfos = [];
   for (let i = 0; i < requestList.length; i++) {
+    const response = await cache.match(requestList[i]);
+    const body = await response.json();
     requestInfos[i] = {
       url: requestList[i].url,
+      resultingClientId: body.resultingClientId
     };
   }
   await caches.delete(cacheName);
 
   event.data.port.postMessage({requestInfos});
 }
 
+// Sends the results of clients.get(id) from this worker. The
+// input is:
+// {
+//   actual_ids: {a: id1, b: id2, x: id3}
+// }
+//
+// The output is:
+// {
+//   clients: {
+//     a: {found: false},
+//     b: {found: false},
+//     x: {
+//       id: id3,
+//       url: url1,
+//       found: true
+//    }
+//   }
+// }
+async function getClients(event) {
+  // |actual_ids| is like:
+  // {a: id1, b: id2, x: id3}
+  const actual_ids = event.data.actual_ids;
+  const result = {}
+  for (let key of Object.keys(actual_ids)) {
+    const id = actual_ids[key];
+    const client = await self.clients.get(id);
+    if (client === undefined)
+      result[key] = {found: false};
+    else
+      result[key] = {found: true, url: client.url, id: client.id};
+  }
+  event.data.port.postMessage({clients: result});
+}
+
 self.addEventListener('message', async function(event) {
   if (event.data.command == 'getRequestInfos') {
     event.waitUntil(getRequestInfos(event));
     return;
   }
 
-  // TODO(falken): Add a getClientInfos command to test Clients API.
+  if (event.data.command == 'getClients') {
+    event.waitUntil(getClients(event));
+    return;
+  }
 });
 
 function get_query_params(url) {
   var search = (new URL(url)).search;
   if (!search) {
     return {};
   }
   var ret = {};
@@ -44,17 +91,21 @@ function get_query_params(url) {
       var element = param.split('=');
       ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
     });
   return ret;
 }
 
 self.addEventListener('fetch', function(event) {
     var waitUntilPromise = caches.open(cacheName).then(function(cache) {
-      return cache.put(event.request, new Response());
+      const responseBody = {};
+      responseBody['resultingClientId'] = event.resultingClientId;
+      const headers = new Headers({'Content-Type': 'application/json'});
+      const response = new Response(JSON.stringify(responseBody), {headers});
+      return cache.put(event.request, response);
     });
     event.waitUntil(waitUntilPromise);
 
     var params = get_query_params(event.request.url);
     if (!params['sw']) {
       // To avoid races, add the waitUntil() promise to our global list.
       // If we get a message event before we finish here, it will wait
       // these promises to complete before proceeding to read from the
--- a/testing/web-platform/tests/speech-api/SpeechSynthesis-speak-without-activation-fails.tentative.html
+++ b/testing/web-platform/tests/speech-api/SpeechSynthesis-speak-without-activation-fails.tentative.html
@@ -3,14 +3,14 @@
 <script src="/resources/testharnessreport.js"></script>
 <body>
 <script>
 // TODO(csharrison): Make this test not tentative once
 // https://github.com/w3c/speech-api/issues/35 is resolved.
 async_test(t => {
   const utter = new SpeechSynthesisUtterance('1');
   utter.onerror = t.step_func_done((e) => {
-    assert_equals(e.name, "not-allowed");
+    assert_equals(e.error, "not-allowed");
   });
   utter.onend = t.step_func_done(() => assert_unreached());
   speechSynthesis.speak(utter);
 }, 'speechSynthesis.speak requires user activation');
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/speech-api/SpeechSynthesisErrorEvent-constructor.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+/*
+[Exposed=Window,
+ Constructor(DOMString type, SpeechSynthesisErrorEventInit eventInitDict)]
+interface SpeechSynthesisErrorEvent : SpeechSynthesisErrorEvent {
+    readonly attribute SpeechSynthesisErrorCode error;
+};
+*/
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisErrorEvent();
+  });
+}, "SpeechSynthesisErrorEvent with no arguments throws TypeError");
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisErrorEvent("type");
+  });
+}, "SpeechSynthesisErrorEvent with no eventInitDict throws TypeError");
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisErrorEvent("type", {});
+  });
+}, `SpeechSynthesisErrorEvent with empty eventInitDict throws TypeError (requires
+    utterance and error)`);
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisErrorEvent("type", {error:"not-allowed"});
+  });
+}, `SpeechSynthesisErrorEvent with eventInitDict without utterance throws
+    TypeError`);
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisErrorEvent("type", {utterance: new SpeechSynthesisUtterance()});
+  });
+}, `SpeechSynthesisErrorEvent with eventInitDict without error throws
+    TypeError`);
+
+test(() => {
+  const utterance = new SpeechSynthesisUtterance("foo");
+  const event = new SpeechSynthesisErrorEvent("type", {utterance: utterance, error:"not-allowed"});
+  assert_equals(event.utterance, utterance);
+  assert_equals(event.error, "not-allowed");
+  assert_equals(event.charIndex, 0);
+  assert_equals(event.elapsedTime, 0);
+  assert_equals(event.name, "");
+}, "SpeechSynthesisErrorEvent with eventInitDict having utterance and error");
+
+test(() => {
+  const utterance = new SpeechSynthesisUtterance("foo");
+  const event = new SpeechSynthesisErrorEvent("type", {
+    utterance: utterance,
+    charIndex: 5,
+    elapsedTime: 100,
+    name: "foo",
+    error: "synthesis-failed"
+  });
+  assert_equals(event.bubbles, false);
+  assert_equals(event.cancelable, false);
+  assert_equals(event.type, "type");
+  assert_equals(event.utterance, utterance);
+  assert_equals(event.charIndex, 5);
+  assert_equals(event.elapsedTime, 100);
+  assert_equals(event.name, "foo");
+  assert_equals(event.error, "synthesis-failed");
+}, "SpeechSynthesisErrorEvent with custom eventInitDict");
+
+test(() => {
+  function createEventFunc(error) {
+    return () => {
+      new SpeechSynthesisErrorEvent("type", {
+        utterance: new SpeechSynthesisUtterance(),
+        error: error
+      });
+    };
+  };
+  assert_throws(new TypeError(), createEventFunc(""));
+  assert_throws(new TypeError(), createEventFunc("foo"));
+  assert_throws(new TypeError(), createEventFunc("bar"));
+}, "SpeechSynthesisErrorEvent with wrong error enum");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/speech-api/SpeechSynthesisEvent-constructor.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+/*
+[Exposed=Window,
+ Constructor(DOMString type, SpeechSynthesisEventInit eventInitDict)]
+interface SpeechSynthesisEvent : Event {
+    readonly attribute SpeechSynthesisUtterance utterance;
+    readonly attribute unsigned long charIndex;
+    readonly attribute float elapsedTime;
+    readonly attribute DOMString name;
+};
+*/
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisEvent();
+  });
+}, "SpeechSynthesisEvent with no arguments throws TypeError");
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisEvent("type");
+  });
+}, "SpeechSynthesisEvent with no eventInitDict throws TypeError");
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisEvent("type", {});
+  });
+}, `SpeechSynthesisEvent with empty eventInitDict throws TypeError (requires
+    utterance)`);
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new SpeechSynthesisEvent("type", {charIndex: 10, elapsedTime: 50, name:"foo"});
+  });
+}, `SpeechSynthesisEvent with eventInitDict not having utterance throws
+    TypeError`);
+
+test(() => {
+  const utterance = new SpeechSynthesisUtterance("foo");
+  const event = new SpeechSynthesisEvent("type", {utterance: utterance});
+  assert_equals(event.utterance, utterance);
+  assert_equals(event.charIndex, 0);
+  assert_equals(event.elapsedTime, 0);
+  assert_equals(event.name, "");
+}, "SpeechSynthesisEvent with eventInitDict having an utterance");
+
+test(() => {
+  const utterance = new SpeechSynthesisUtterance("foo");
+  const event = new SpeechSynthesisEvent("type", {
+    utterance: utterance,
+    charIndex: 5,
+    elapsedTime: 100,
+    name: "foo"
+  });
+  assert_equals(event.bubbles, false);
+  assert_equals(event.cancelable, false);
+  assert_equals(event.type, "type");
+  assert_equals(event.utterance, utterance);
+  assert_equals(event.charIndex, 5);
+  assert_equals(event.elapsedTime, 100);
+  assert_equals(event.name, "foo");
+}, "SpeechSynthesisEvent with custom eventInitDict");
+</script>
--- a/testing/web-platform/tests/speech-api/SpeechSynthesisUtterance-basics.https.html
+++ b/testing/web-platform/tests/speech-api/SpeechSynthesisUtterance-basics.https.html
@@ -26,15 +26,26 @@ test(function() {
   for (const prop in DEFAULTS) {
     if (prop != 'text') {
       assert_equals(utt[prop], DEFAULTS[prop], prop);
     }
   }
 }, 'new SpeechSynthesisUtterance("hello") text and defaults');
 
 test(function() {
+  const utt = new SpeechSynthesisUtterance(null);
+  assert_equals(utt.text, 'null');
+}, 'new SpeechSynthesisUtterance(null)');
+
+test(function() {
+  const utt = new SpeechSynthesisUtterance(undefined);
+  // See https://github.com/w3c/speech-api/pull/48.
+  assert_equals(utt.text, '');
+}, 'new SpeechSynthesisUtterance(undefined)');
+
+test(function() {
   const utt = new SpeechSynthesisUtterance();
   utt.text = 'word';
   assert_equals(utt.text, 'word');
 }, 'SpeechSynthesisUtterance text setter');
 
 // TODO: setters https://github.com/w3c/speech-api/issues/29
 </script>
--- a/testing/web-platform/tests/tools/manifest/sourcefile.py
+++ b/testing/web-platform/tests/tools/manifest/sourcefile.py
@@ -262,16 +262,17 @@ class SourceFile(object):
     @property
     def name_is_non_test(self):
         """Check if the file name matches the conditions for the file to
         be a non-test file"""
         return (self.is_dir() or
                 self.name_prefix("MANIFEST") or
                 self.filename == "META.yml" or
                 self.filename.startswith(".") or
+                self.filename.endswith(".headers") or
                 self.type_flag == "support" or
                 self.in_non_test_dir())
 
     @property
     def name_is_conformance(self):
         return (self.in_conformance_checker_dir() and
                 self.type_flag in ("is-valid", "no-valid"))
 
--- a/testing/web-platform/tests/tools/manifest/tests/test_sourcefile.py
+++ b/testing/web-platform/tests/tools/manifest/tests/test_sourcefile.py
@@ -31,16 +31,17 @@ def items(s):
     "conformance-checkers/test.html",
     "conformance-checkers/README.md",
     "conformance-checkers/html/Makefile",
     "conformance-checkers/html/test.html",
     "foo/tools/test.html",
     "foo/resources/test.html",
     "foo/support/test.html",
     "foo/test-support.html",
+    "foo/foo-manual.html.headers",
     "css/common/test.html",
     "css/CSS2/archive/test.html",
 ])
 def test_name_is_non_test(rel_path):
     s = create(rel_path)
     assert s.name_is_non_test or s.name_is_conformance_support
 
     assert not s.content_is_testharness
@@ -59,16 +60,17 @@ def test_name_is_non_test(rel_path):
 def test_not_name_is_non_test(rel_path):
     s = create(rel_path)
     assert not (s.name_is_non_test or s.name_is_conformance_support)
     # We aren't actually asserting what type of test these are, just their
     # name doesn't prohibit them from being tests.
 
 
 @pytest.mark.parametrize("rel_path", [
+    "foo/foo-manual.html",
     "html/test-manual.html",
     "html/test-manual.xhtml",
     "html/test-manual.https.html",
     "html/test-manual.https.xhtml"
 ])
 def test_name_is_manual(rel_path):
     s = create(rel_path)
     assert not s.name_is_non_test
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorselenium.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorselenium.py
@@ -235,30 +235,34 @@ class SeleniumRun(object):
             self.logger.error("Lost WebDriver connection")
             return Stop
 
         executor = threading.Thread(target=self._run)
         executor.start()
 
         flag = self.result_flag.wait(timeout + 2 * extra_timeout)
         if self.result is None:
-            assert not flag
-            self.result = False, ("EXTERNAL-TIMEOUT", None)
+            if flag:
+                # flag is True unless we timeout; this *shouldn't* happen, but
+                # it can if self._run fails to set self.result due to raising
+                self.result = False, ("INTERNAL-ERROR", "self._run didn't set a result")
+            else:
+                self.result = False, ("EXTERNAL-TIMEOUT", None)
 
         return self.result
 
     def _run(self):
         try:
             self.result = True, self.func(self.protocol, self.url, self.timeout)
         except exceptions.TimeoutException:
             self.result = False, ("EXTERNAL-TIMEOUT", None)
         except (socket.timeout, exceptions.ErrorInResponseException):
             self.result = False, ("CRASH", None)
         except Exception as e:
-            message = getattr(e, "message", "")
+            message = str(getattr(e, "message", ""))
             if message:
                 message += "\n"
             message += traceback.format_exc(e)
             self.result = False, ("INTERNAL-ERROR", e)
         finally:
             self.result_flag.set()
 
 
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -223,18 +223,22 @@ class WebDriverRun(object):
             self.logger.error("Lost WebDriver connection")
             return Stop
 
         executor = threading.Thread(target=self._run)
         executor.start()
 
         flag = self.result_flag.wait(timeout + 2 * extra_timeout)
         if self.result is None:
-            assert not flag
-            self.result = False, ("EXTERNAL-TIMEOUT", None)
+            if flag:
+                # flag is True unless we timeout; this *shouldn't* happen, but
+                # it can if self._run fails to set self.result due to raising
+                self.result = False, ("INTERNAL-ERROR", "self._run didn't set a result")
+            else:
+                self.result = False, ("EXTERNAL-TIMEOUT", None)
 
         return self.result
 
     def _run(self):
         try:
             self.result = True, self.func(self.protocol, self.url, self.timeout)
         except (client.TimeoutException, client.ScriptTimeoutException):
             self.result = False, ("EXTERNAL-TIMEOUT", None)
@@ -242,21 +246,21 @@ class WebDriverRun(object):
             self.result = False, ("CRASH", None)
         except Exception as e:
             if (isinstance(e, client.WebDriverException) and
                     e.http_status == 408 and
                     e.status_code == "asynchronous script timeout"):
                 # workaround for https://bugs.chromium.org/p/chromedriver/issues/detail?id=2001
                 self.result = False, ("EXTERNAL-TIMEOUT", None)
             else:
-                message = getattr(e, "message", "")
+                message = str(getattr(e, "message", ""))
                 if message:
                     message += "\n"
                 message += traceback.format_exc(e)
-                self.result = False, ("ERROR", message)
+                self.result = False, ("INTERNAL-ERROR", message)
         finally:
             self.result_flag.set()
 
 
 class WebDriverTestharnessExecutor(TestharnessExecutor):
     supports_testdriver = True
 
     def __init__(self, browser, server_config, timeout_multiplier=1,
--- a/testing/web-platform/tests/tools/wptserve/tests/functional/base.py
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/base.py
@@ -70,17 +70,17 @@ class TestUsingServer(unittest.TestCase)
 
         for name, value in iteritems(headers):
             req.add_header(name, value)
 
         if body is not None:
             req.add_data(body)
 
         if auth is not None:
-            req.add_header("Authorization", b"Basic %s" % base64.b64encode(("%s:%s" % auth).encode("utf-8")))
+            req.add_header("Authorization", "Basic %s" % base64.b64encode('%s:%s' % auth))
 
         return urlopen(req)
 
 
 @pytest.mark.skipif(not wptserve.utils.http2_compatible(), reason="h2 server only works in python 2.7.15")
 class TestUsingH2Server:
     def setup_method(self, test_method):
         self.server = wptserve.server.WebTestHttpd(host="localhost",
--- a/testing/web-platform/tests/tools/wptserve/tests/functional/test_handlers.py
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/test_handlers.py
@@ -83,26 +83,29 @@ class TestFileHandler(TestUsingServer):
             self.request("/document.txt", headers={"Range":"bytes=11-10"})
         self.assertEqual(cm.exception.code, 416)
 
         expected = open(os.path.join(doc_root, "document.txt"), 'rb').read()
         with self.assertRaises(HTTPError) as cm:
             self.request("/document.txt", headers={"Range":"bytes=%i-%i" % (len(expected), len(expected) + 10)})
         self.assertEqual(cm.exception.code, 416)
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_config(self):
         resp = self.request("/sub.sub.txt")
         expected = b"localhost localhost %i" % self.server.port
         assert resp.read().rstrip() == expected
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_headers(self):
         resp = self.request("/sub_headers.sub.txt", headers={"X-Test": "PASS"})
         expected = b"PASS"
         assert resp.read().rstrip() == expected
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_params(self):
         resp = self.request("/sub_params.sub.txt", query="test=PASS")
         expected = b"PASS"
         assert resp.read().rstrip() == expected
 
 
 class TestFunctionHandler(TestUsingServer):
     def test_string_rv(self):
--- a/testing/web-platform/tests/tools/wptserve/tests/functional/test_pipes.py
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/test_pipes.py
@@ -52,76 +52,84 @@ class TestSlice(TestUsingServer):
         self.assertEqual(resp.read(), expected[1:])
 
     def test_no_lower(self):
         resp = self.request("/document.txt", query="pipe=slice(null,10)")
         expected = open(os.path.join(doc_root, "document.txt"), 'rb').read()
         self.assertEqual(resp.read(), expected[:10])
 
 class TestSub(TestUsingServer):
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_config(self):
         resp = self.request("/sub.txt", query="pipe=sub")
-        expected = b"localhost localhost %i" % self.server.port
+        expected = "localhost localhost %i" % self.server.port
         self.assertEqual(resp.read().rstrip(), expected)
 
     @pytest.mark.xfail(sys.platform == "win32",
                        reason="https://github.com/web-platform-tests/wpt/issues/12949")
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_file_hash(self):
         resp = self.request("/sub_file_hash.sub.txt")
-        expected = b"""
+        expected = """
 md5: JmI1W8fMHfSfCarYOSxJcw==
 sha1: nqpWqEw4IW8NjD6R375gtrQvtTo=
 sha224: RqQ6fMmta6n9TuA/vgTZK2EqmidqnrwBAmQLRQ==
 sha256: G6Ljg1uPejQxqFmvFOcV/loqnjPTW5GSOePOfM/u0jw=
 sha384: lkXHChh1BXHN5nT5BYhi1x67E1CyYbPKRKoF2LTm5GivuEFpVVYtvEBHtPr74N9E
-sha512: r8eLGRTc7ZznZkFjeVLyo6/FyQdra9qmlYCwKKxm3kfQAswRS9+3HsYk3thLUhcFmmWhK4dXaICzJwGFonfXwg=="""
+sha512: r8eLGRTc7ZznZkFjeVLyo6/FyQdra9qmlYCwKKxm3kfQAswRS9+3HsYk3thLUhcFmmWhK4dXaICz
+JwGFonfXwg=="""
         self.assertEqual(resp.read().rstrip(), expected.strip())
 
     def test_sub_file_hash_unrecognized(self):
         with self.assertRaises(urllib.error.HTTPError):
             self.request("/sub_file_hash_unrecognized.sub.txt")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_headers(self):
         resp = self.request("/sub_headers.txt", query="pipe=sub", headers={"X-Test": "PASS"})
-        expected = b"PASS"
+        expected = "PASS"
         self.assertEqual(resp.read().rstrip(), expected)
 
     @pytest.mark.xfail(sys.platform == "win32",
                        reason="https://github.com/web-platform-tests/wpt/issues/12949")
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_location(self):
         resp = self.request("/sub_location.sub.txt?query_string")
         expected = """
 host: localhost:{0}
 hostname: localhost
 path: /sub_location.sub.txt
 pathname: /sub_location.sub.txt
 port: {0}
 query: ?query_string
 scheme: http
-server: http://localhost:{0}""".format(self.server.port).encode("ascii")
+server: http://localhost:{0}""".format(self.server.port)
         self.assertEqual(resp.read().rstrip(), expected.strip())
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_params(self):
         resp = self.request("/sub_params.txt", query="test=PASS&pipe=sub")
-        expected = b"PASS"
+        expected = "PASS"
         self.assertEqual(resp.read().rstrip(), expected)
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_url_base(self):
         resp = self.request("/sub_url_base.sub.txt")
-        self.assertEqual(resp.read().rstrip(), b"Before / After")
+        self.assertEqual(resp.read().rstrip(), "Before / After")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_uuid(self):
         resp = self.request("/sub_uuid.sub.txt")
-        self.assertRegexpMatches(resp.read().rstrip(), b"Before [a-f0-9-]+ After")
+        self.assertRegexpMatches(resp.read().rstrip(), r"Before [a-f0-9-]+ After")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_sub_var(self):
         resp = self.request("/sub_var.sub.txt")
         port = self.server.port
-        print(port, type(port))
-        expected = b"localhost %d A %d B localhost C" % (port, port)
+        expected = "localhost %s A %s B localhost C" % (port, port)
         self.assertEqual(resp.read().rstrip(), expected)
 
 class TestTrickle(TestUsingServer):
     def test_trickle(self):
         #Actually testing that the response trickles in is not that easy
         t0 = time.time()
         resp = self.request("/document.txt", query="pipe=trickle(1:d2:5:d1:r2)")
         t1 = time.time()
@@ -131,76 +139,84 @@ class TestTrickle(TestUsingServer):
 
     def test_headers(self):
         resp = self.request("/document.txt", query="pipe=trickle(d0.01)")
         self.assertEqual(resp.info()["Cache-Control"], "no-cache, no-store, must-revalidate")
         self.assertEqual(resp.info()["Pragma"], "no-cache")
         self.assertEqual(resp.info()["Expires"], "0")
 
 class TestPipesWithVariousHandlers(TestUsingServer):
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_with_python_file_handler(self):
         resp = self.request("/test_string.py", query="pipe=slice(null,2)")
-        self.assertEqual(resp.read(), b"PA")
+        self.assertEqual(resp.read(), "PA")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_with_python_func_handler(self):
         @wptserve.handlers.handler
         def handler(request, response):
             return "PASS"
         route = ("GET", "/test/test_pipes_1/", handler)
         self.server.router.register(*route)
         resp = self.request(route[1], query="pipe=slice(null,2)")
-        self.assertEqual(resp.read(), b"PA")
+        self.assertEqual(resp.read(), "PA")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_with_python_func_handler_using_response_writer(self):
         @wptserve.handlers.handler
         def handler(request, response):
             response.writer.write_content("PASS")
         route = ("GET", "/test/test_pipes_1/", handler)
         self.server.router.register(*route)
         resp = self.request(route[1], query="pipe=slice(null,2)")
         # slice has not been applied to the response, because response.writer was used.
-        self.assertEqual(resp.read(), b"PASS")
+        self.assertEqual(resp.read(), "PASS")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_header_pipe_with_python_func_using_response_writer(self):
         @wptserve.handlers.handler
         def handler(request, response):
             response.writer.write_content("CONTENT")
         route = ("GET", "/test/test_pipes_1/", handler)
         self.server.router.register(*route)
         resp = self.request(route[1], query="pipe=header(X-TEST,FAIL)")
         # header pipe was ignored, because response.writer was used.
         self.assertFalse(resp.info().get("X-TEST"))
-        self.assertEqual(resp.read(), b"CONTENT")
+        self.assertEqual(resp.read(), "CONTENT")
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_with_json_handler(self):
         @wptserve.handlers.json_handler
         def handler(request, response):
             return json.dumps({'data': 'PASS'})
         route = ("GET", "/test/test_pipes_2/", handler)
         self.server.router.register(*route)
         resp = self.request(route[1], query="pipe=slice(null,2)")
-        self.assertEqual(resp.read(), b'"{')
+        self.assertEqual(resp.read(), '"{')
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_slice_with_as_is_handler(self):
         resp = self.request("/test.asis", query="pipe=slice(null,2)")
         self.assertEqual(202, resp.getcode())
         self.assertEqual("Giraffe", resp.msg)
         self.assertEqual("PASS", resp.info()["X-Test"])
         # slice has not been applied to the response, because response.writer was used.
-        self.assertEqual(b"Content", resp.read())
+        self.assertEqual("Content", resp.read())
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_headers_with_as_is_handler(self):
         resp = self.request("/test.asis", query="pipe=header(X-TEST,FAIL)")
         self.assertEqual(202, resp.getcode())
         self.assertEqual("Giraffe", resp.msg)
         # header pipe was ignored.
         self.assertEqual("PASS", resp.info()["X-TEST"])
-        self.assertEqual(b"Content", resp.read())
+        self.assertEqual("Content", resp.read())
 
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_trickle_with_as_is_handler(self):
         t0 = time.time()
         resp = self.request("/test.asis", query="pipe=trickle(1:d2:5:d1:r2)")
         t1 = time.time()
-        self.assertTrue(b'Content' in resp.read())
+        self.assertTrue('Content' in resp.read())
         self.assertGreater(6, t1-t0)
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/web-platform/tests/tools/wptserve/tests/functional/test_request.py
+++ b/testing/web-platform/tests/tools/wptserve/tests/functional/test_request.py
@@ -1,8 +1,10 @@
+import sys
+
 import pytest
 
 wptserve = pytest.importorskip("wptserve")
 from .base import TestUsingServer
 from wptserve.request import InputFile
 
 
 class TestInputFile(TestUsingServer):
@@ -110,18 +112,19 @@ class TestRequest(TestUsingServer):
 
         route = ("GET", "/test/{match}_*", handler)
         self.server.router.register(*route)
         resp = self.request("/test/some_route")
         self.assertEqual(b"some route", resp.read())
 
 
 class TestAuth(TestUsingServer):
+    @pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
     def test_auth(self):
         @wptserve.handlers.handler
         def handler(request, response):
             return " ".join((request.auth.username, request.auth.password))
 
         route = ("GET", "/test/test_auth", handler)
         self.server.router.register(*route)
         resp = self.request(route[1], auth=("test", "PASS"))
         self.assertEqual(200, resp.getcode())
-        self.assertEqual([b"test", b"PASS"], resp.read().split(b" "))
+        self.assertEqual(["test", "PASS"], resp.read().split(" "))
--- a/testing/web-platform/tests/tools/wptserve/wptserve/pipes.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/pipes.py
@@ -1,11 +1,10 @@
 from cgi import escape
 from collections import deque
-import base64
 import gzip as gzip_module
 import hashlib
 import os
 import re
 import time
 import uuid
 from six.moves import StringIO
 
@@ -269,19 +268,18 @@ def slice(request, response, start, end=
 
     :param start: The starting offset. Follows python semantics including
                   negative numbers.
 
     :param end: The ending offset, again with python semantics and None
                 (spelled "null" in a query string) to indicate the end of
                 the file.
     """
-    content = resolve_content(response)[start:end]
-    response.content = content
-    response.headers.set("Content-Length", len(content))
+    content = resolve_content(response)
+    response.content = content[start:end]
     return response
 
 
 class ReplacementTokenizer(object):
     def arguments(self, token):
         unwrapped = token[1:-1].decode('utf8')
         return ("arguments", re.split(r",\s*", unwrapped) if unwrapped else [])
 
@@ -389,49 +387,50 @@ class SubFunctions(object):
     # are available on all platforms [1]. This ensures that test authors do not
     # unknowingly introduce platform-specific tests.
     #
     # [1] https://docs.python.org/2/library/hashlib.html
     supported_algorithms = ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")
 
     @staticmethod
     def file_hash(request, algorithm, path):
-        assert isinstance(algorithm, text_type)
+        algorithm = algorithm.decode("ascii")
         if algorithm not in SubFunctions.supported_algorithms:
             raise ValueError("Unsupported encryption algorithm: '%s'" % algorithm)
 
         hash_obj = getattr(hashlib, algorithm)()
         absolute_path = os.path.join(request.doc_root, path)
 
         try:
-            with open(absolute_path, "rb") as f:
+            with open(absolute_path) as f:
                 hash_obj.update(f.read())
         except IOError:
             # In this context, an unhandled IOError will be interpreted by the
             # server as an indication that the template file is non-existent.
             # Although the generic "Exception" is less precise, it avoids
             # triggering a potentially-confusing HTTP 404 error in cases where
             # the path to the file to be hashed is invalid.
             raise Exception('Cannot open file for hash computation: "%s"' % absolute_path)
 
-        return base64.b64encode(hash_obj.digest()).strip()
+        return hash_obj.digest().encode('base64').strip()
 
 def template(request, content, escape_type="html"):
     #TODO: There basically isn't any error handling here
     tokenizer = ReplacementTokenizer()
 
     variables = {}
 
     def config_replacement(match):
         content, = match.groups()
 
         tokens = tokenizer.tokenize(content)
         tokens = deque(tokens)
 
         token_type, field = tokens.popleft()
+        field = field.decode("ascii")
 
         if token_type == "var":
             variable = field
             token_type, field = tokens.popleft()
         else:
             variable = None
 
         if token_type != "ident":
@@ -486,21 +485,17 @@ def template(request, content, escape_ty
         if variable is not None:
             variables[variable] = value
 
         escape_func = {"html": lambda x:escape(x, quote=True),
                        "none": lambda x:x}[escape_type]
 
         #Should possibly support escaping for other contexts e.g. script
         #TODO: read the encoding of the response
-        if isinstance(value, binary_type):
-            value = value.decode("utf-8")
-        elif isinstance(value, int):
-            value = text_type(value)
-        return escape_func(value).encode("utf-8")
+        return escape_func(text_type(value)).encode("utf-8")
 
     template_regexp = re.compile(br"{{([^}]*)}}")
     new_content = template_regexp.sub(config_replacement, content)
 
     return new_content
 
 @pipe()
 def gzip(request, response):
--- a/testing/web-platform/tests/tools/wptserve/wptserve/request.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/request.py
@@ -1,12 +1,12 @@
 import base64
 import cgi
 from six.moves.http_cookies import BaseCookie
-from six import BytesIO, binary_type, text_type
+from six import BytesIO
 import tempfile
 
 from six.moves.urllib.parse import parse_qsl, urlsplit
 
 from . import stash
 from .utils import HTTPException
 
 missing = object()
@@ -313,18 +313,18 @@ class Request(object):
             self._POST = MultiDict.from_field_storage(fs)
             self.raw_input.seek(pos)
         return self._POST
 
     @property
     def cookies(self):
         if self._cookies is None:
             parser = BaseCookie()
-            cookie_headers = self.headers.get("cookie", u"")
-            parser.load(cookie_headers.encode("utf-8"))
+            cookie_headers = self.headers.get("cookie", "")
+            parser.load(cookie_headers)
             cookies = Cookies()
             for key, value in parser.iteritems():
                 cookies[key] = CookieValue(value)
             self._cookies = cookies
         return self._cookies
 
     @property
     def headers(self):
@@ -350,51 +350,39 @@ class Request(object):
 
 class H2Request(Request):
     def __init__(self, request_handler):
         self.h2_stream_id = request_handler.h2_stream_id
         self.frames = []
         super(H2Request, self).__init__(request_handler)
 
 
-def maybedecode(s):
-    if isinstance(s, text_type):
-        return s
-
-    if isinstance(s, binary_type):
-        return s.decode("ascii")
-
-    raise TypeError("Unexpected value in RequestHeaders: %r" % s)
-
-
 class RequestHeaders(dict):
     """Dictionary-like API for accessing request headers."""
     def __init__(self, items):
         for header in items.keys():
             key = header.lower()
             # get all headers with the same name
             values = items.getallmatchingheaders(header)
             if len(values) > 1:
                 # collect the multiple variations of the current header
                 multiples = []
                 # loop through the values from getallmatchingheaders
                 for value in values:
                     # getallmatchingheaders returns raw header lines, so
                     # split to get name, value
-                    multiples.append(maybedecode(value).split(':', 1)[1].strip())
-                headers = multiples
+                    multiples.append(value.split(':', 1)[1].strip())
+                dict.__setitem__(self, key, multiples)
             else:
-                headers = [maybedecode(items[header])]
-            dict.__setitem__(self, maybedecode(key), headers)
+                dict.__setitem__(self, key, [items[header]])
 
 
     def __getitem__(self, key):
         """Get all headers of a certain (case-insensitive) name. If there is
         more than one, the values are returned comma separated"""
-        key = maybedecode(key)
         values = dict.__getitem__(self, key.lower())
         if len(values) == 1:
             return values[0]
         else:
             return ", ".join(values)
 
     def __setitem__(self, name, value):
         raise Exception
@@ -410,27 +398,25 @@ class RequestHeaders(dict):
         try:
             return self[key]
         except KeyError:
             return default
 
     def get_list(self, key, default=missing):
         """Get all the header values for a particular field name as
         a list"""
-        key = maybedecode(key)
         try:
             return dict.__getitem__(self, key.lower())
         except KeyError:
             if default is not missing:
                 return default
             else:
                 raise
 
     def __contains__(self, key):
-        key = maybedecode(key)
         return dict.__contains__(self, key.lower())
 
     def iteritems(self):
         for item in self:
             yield item, self[item]
 
     def itervalues(self):
         for item in self:
@@ -608,19 +594,17 @@ class Authentication(object):
     def __init__(self, headers):
         self.username = None
         self.password = None
 
         auth_schemes = {"Basic": self.decode_basic}
 
         if "authorization" in headers:
             header = headers.get("authorization")
-            assert isinstance(header, text_type)
             auth_type, data = header.split(" ", 1)
             if auth_type in auth_schemes:
                 self.username, self.password = auth_schemes[auth_type](data)
             else:
                 raise HTTPException(400, "Unsupported authentication scheme %s" % auth_type)
 
     def decode_basic(self, data):
-        assert isinstance(data, text_type)
-        decoded_data = base64.decodestring(data.encode("utf-8"))
-        return decoded_data.decode("utf-8").split(":", 1)
+        decoded_data = base64.decodestring(data)
+        return decoded_data.split(":", 1)
--- a/testing/web-platform/tests/tools/wptserve/wptserve/response.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/response.py
@@ -178,20 +178,18 @@ class Response(object):
         and the resulting value (if any) returned.
 
         :param read_file: - boolean controlling the behaviour when content
         is a file handle. When set to False the handle will be returned directly
         allowing the file to be passed to the output in small chunks. When set to
         True, the entire content of the file will be returned as a string facilitating
         non-streaming operations like template substitution.
         """
-        if isinstance(self.content, binary_type):
+        if isinstance(self.content, (binary_type, text_type)):
             yield self.content
-        elif isinstance(self.content, text_type):
-            yield self.content.encode("utf-8")
         elif hasattr(self.content, "read"):
             if read_file:
                 yield self.content.read()
             else:
                 yield self.content
         else:
             for item in self.content:
                 if hasattr(item, "__call__"):
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCIceTransport-extension-helper.js
@@ -0,0 +1,42 @@
+'use strict';
+
+// Construct an RTCIceTransport instance. The instance will automatically be
+// cleaned up when the test finishes.
+function makeIceTransport(t) {
+  const iceTransport = new RTCIceTransport();
+  t.add_cleanup(() => iceTransport.stop());
+  return iceTransport;
+}
+
+// Construct two RTCIceTransport instances, configure them to exchange
+// candidates, then gather() them.
+// Returns a 2-list: [ RTCIceTransport, RTCIceTransport ]
+function makeAndGatherTwoIceTransports(t) {
+  const localTransport = makeIceTransport(t);
+  const remoteTransport = makeIceTransport(t);
+  localTransport.onicecandidate = e => {
+    if (e.candidate) {
+      remoteTransport.addRemoteCandidate(e.candidate);
+    }
+  };
+  remoteTransport.onicecandidate = e => {
+    if (e.candidate) {
+      localTransport.addRemoteCandidate(e.candidate);
+    }
+  };
+  localTransport.gather({});
+  remoteTransport.gather({});
+  return [ localTransport, remoteTransport ];
+}
+
+// Construct two RTCIceTransport instances, configure them to exchange
+// candidates and parameters, then gather() and start() them.
+// Returns a 2-list:
+//     [ controlling RTCIceTransport,
+//       controlled RTCIceTransport ]
+function makeGatherAndStartTwoIceTransports(t) {
+  const [ localTransport, remoteTransport ] = makeAndGatherTwoIceTransports(t);
+  localTransport.start(remoteTransport.getLocalParameters(), 'controlling');
+  remoteTransport.start(localTransport.getLocalParameters(), 'controlled');
+  return [ localTransport, remoteTransport ];
+}
--- a/testing/web-platform/tests/webrtc/RTCIceTransport-extension.https.html
+++ b/testing/web-platform/tests/webrtc/RTCIceTransport-extension.https.html
@@ -1,19 +1,25 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCIceTransport-extensions.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following extension specification:
 // https://w3c.github.io/webrtc-ice/
 
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeGatherAndStartTwoIceTransports
+
 function makeIceTransport(t) {
   const iceTransport = new RTCIceTransport();
   t.add_cleanup(() => iceTransport.stop());
   return iceTransport;
 }
 
 test(() => {
   const iceTransport = new RTCIceTransport();
@@ -235,32 +241,18 @@ test(t => {
   assert_equals(iceTransport.state, 'new');
   assert_array_equals(iceTransport.getRemoteCandidates(), []);
   assert_ice_parameters_equals(iceTransport.getRemoteParameters(),
       changedRemoteParameters);
 }, `start() flushes remote candidates and transitions state to 'new' if ` +
    'later called with different remote parameters');
 
 promise_test(async t => {
-  const localTransport = makeIceTransport(t);
-  const remoteTransport = makeIceTransport(t);
-  localTransport.onicecandidate = e => {
-    if (e.candidate) {
-      remoteTransport.addRemoteCandidate(e.candidate);
-    }
-  };
-  remoteTransport.onicecandidate = e => {
-    if (e.candidate) {
-      localTransport.addRemoteCandidate(e.candidate);
-    }
-  };
-  localTransport.gather({});
-  remoteTransport.gather({});
-  localTransport.start(remoteTransport.getLocalParameters(), 'controlling');
-  remoteTransport.start(localTransport.getLocalParameters(), 'controlled');
+  const [ localTransport, remoteTransport ] =
+      makeGatherAndStartTwoIceTransports(t);
   const localWatcher = new EventWatcher(t, localTransport, 'statechange');
   const remoteWatcher = new EventWatcher(t, remoteTransport, 'statechange');
   await Promise.all([
     localWatcher.wait_for('statechange').then(() => {
       assert_equals(localTransport.state, 'connected');
     }),
     remoteWatcher.wait_for('statechange').then(() => {
       assert_equals(remoteTransport.state, 'connected');
--- a/testing/web-platform/tests/webrtc/RTCQuicStream.https.html
+++ b/testing/web-platform/tests/webrtc/RTCQuicStream.https.html
@@ -1,43 +1,44 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCQuicStream.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script src="RTCQuicTransport-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following specification:
 // https://w3c.github.io/webrtc-quic/
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
-//   makeQuicTransport
+//   makeStandaloneQuicTransport
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   const quicStream = quicTransport.createStream();
   assert_equals(quicStream.transport, quicTransport,
       'Expect transport to be set to the creating RTCQuicTransport.');
   assert_equals(quicStream.state, 'new', `Expect state to be 'new'.`);
   assert_equals(quicStream.readBufferedAmount, 0,
       'Expect read buffered amount to be 0.');
   assert_equals(quicStream.writeBufferedAmount, 0,
       'Expect write buffered amount to be 0.');
 }, 'createStream() returns an RTCQuicStream with initial properties set.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_throws('InvalidStateError', () => quicTransport.createStream());
 }, 'createStream() throws if the transport is closed.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   const firstQuicStream = quicTransport.createStream();
   const secondQuicStream = quicTransport.createStream();
   quicTransport.stop();
   assert_equals(firstQuicStream.state, 'closed');
   assert_equals(secondQuicStream.state, 'closed');
 }, 'RTCQuicTransport.stop() closes all streams.');
 
 </script>
--- a/testing/web-platform/tests/webrtc/RTCQuicTransport-helper.js
+++ b/testing/web-platform/tests/webrtc/RTCQuicTransport-helper.js
@@ -1,10 +1,82 @@
 'use strict';
 
-function makeQuicTransport(t, certificates) {
-  const iceTransport = new RTCIceTransport();
-  t.add_cleanup(() => iceTransport.stop());
+// This file depends on RTCIceTransport-extension-helper.js which should be
+// loaded from the main HTML file.
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeGatherAndStartTwoIceTransports
+
+// Return a promise to generate an RTCCertificate with the given keygen
+// algorithm or a default one if none provided.
+function generateCertificate(keygenAlgorithm) {
+  return RTCPeerConnection.generateCertificate({
+    name: 'ECDSA',
+    namedCurve: 'P-256',
+    ...keygenAlgorithm,
+  });
+}
+
+// Construct an RTCQuicTransport instance with the given RTCIceTransport
+// instance and the given certificates. The RTCQuicTransport instance will be
+// automatically cleaned up when the test finishes.
+function makeQuicTransport(t, iceTransport, certificates) {
   const quicTransport = new RTCQuicTransport(iceTransport, certificates);
   t.add_cleanup(() => quicTransport.stop());
   return quicTransport;
 }
 
+// Construct an RTCQuicTransport instance with a new RTCIceTransport instance
+// and a single, newly-generated certificate. The RTCQuicTransport and
+// RTCIceTransport instances will be automatically cleaned up when the test
+// finishes.
+async function makeStandaloneQuicTransport(t) {
+  const certificate = await generateCertificate();
+  return makeQuicTransport(t, makeIceTransport(t), [ certificate ]);
+}
+
+// Construct two RTCQuicTransport instances and each call start() with the other
+// transport's local parameters.
+// Returns a 2-list:
+//     [ server RTCQuicTransport,
+//       client RTCQuicTransport ]
+async function makeAndStartTwoQuicTransports(t) {
+  const [ localCertificate, remoteCertificate ] =
+      await Promise.all([ generateCertificate(), generateCertificate() ]);
+  const [ localIceTransport, remoteIceTransport ] =
+      makeGatherAndStartTwoIceTransports(t);
+  const localQuicTransport =
+      makeQuicTransport(t, localIceTransport, [ localCertificate ]);
+  const remoteQuicTransport =
+      makeQuicTransport(t, remoteIceTransport, [ remoteCertificate ]);
+  localQuicTransport.start(remoteQuicTransport.getLocalParameters());
+  remoteQuicTransport.start(localQuicTransport.getLocalParameters());
+  return [ localQuicTransport, remoteQuicTransport ];
+}
+
+// Construct two RTCQuicTransport instances and wait for them to connect.
+// Returns a 2-list:
+//     [ server RTCQuicTransport,
+//       client RTCQuicTransport ]
+async function makeTwoConnectedQuicTransports(t) {
+  // Returns a promise that resolves when the transport fires a 'statechange'
+  // event to 'connected'.
+  function waitForConnected(transport) {
+    return new Promise((resolve, reject) => {
+      const eventHandler = t.step_func(() => {
+        assert_equals(transport.state, 'connected');
+        transport.removeEventListener('statechange', eventHandler, false);
+        resolve();
+      });
+      transport.addEventListener('statechange', eventHandler, false);
+    });
+  }
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeAndStartTwoQuicTransports(t);
+  await Promise.all([
+    waitForConnected(localQuicTransport),
+    waitForConnected(remoteQuicTransport),
+  ]);
+  return [ localQuicTransport, remoteQuicTransport ];
+}
+
--- a/testing/web-platform/tests/webrtc/RTCQuicTransport.https.html
+++ b/testing/web-platform/tests/webrtc/RTCQuicTransport.https.html
@@ -1,91 +1,188 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCQuicTransport.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script src="RTCQuicTransport-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following specification:
 // https://w3c.github.io/webrtc-quic/
 
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeAndGatherTwoIceTransports
+
 // The following helper functions are called from RTCQuicTransport-helper.js:
 //   makeQuicTransport
-
-function generateCertificate(keygenAlgorithm) {
-  return RTCPeerConnection.generateCertificate({
-    name: 'ECDSA',
-    namedCurve: 'P-256',
-    ...keygenAlgorithm,
-  });
-}
+//   makeStandaloneQuicTransport
+//   makeAndStartTwoQuicTransports
+//   makeTwoConnectedQuicTransports
 
-test(t => {
-  // Don't use the makeQuicTransport helper so that the transport property can
-  // be verified.
-  const iceTransport = new RTCIceTransport();
-  const quicTransport = new RTCQuicTransport(iceTransport, []);
-  t.add_cleanup(() => {
-    quicTransport.stop();
-    iceTransport.stop();
-  });
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
+  const quicTransport = makeQuicTransport(t, iceTransport, [ certificate ]);
   assert_equals(quicTransport.transport, iceTransport,
       'Expect transport to be the same as the one passed in the constructor.');
   assert_equals(quicTransport.state, 'new', `Expect state to be 'new'.`);
   assert_object_equals(quicTransport.getLocalParameters(),
-      { role: 'auto', fingerprints: [] },
+      { role: 'auto', fingerprints: certificate.getFingerprints() },
       'Expect local parameters to be initialized.');
   assert_equals(quicTransport.getRemoteParameters(), null,
       'Expect no remote parameters.');
-  assert_array_equals(quicTransport.getCertificates(), [],
-      'Expect not certificates.');
+  assert_array_equals(quicTransport.getCertificates(), [ certificate ],
+      'Expect one certificate.');
   assert_array_equals(quicTransport.getRemoteCertificates(), [],
       'Expect no remote certificates.');
 }, 'RTCQuicTransport initial properties are set.');
 
 promise_test(async t => {
   const [ firstCertificate, secondCertificate ] =
       await Promise.all([ generateCertificate(), generateCertificate() ]);
   const quicTransport =
-      makeQuicTransport(t, [ firstCertificate, secondCertificate ]);
+      makeQuicTransport(t, makeIceTransport(t),
+          [ firstCertificate, secondCertificate ]);
   assert_array_equals(quicTransport.getCertificates(),
       [ firstCertificate, secondCertificate ]);
 }, 'getCertificates() returns the certificates passed in the constructor.');
 
 promise_test(async t => {
   const [ firstCertificate, secondCertificate ] =
       await Promise.all([ generateCertificate(), generateCertificate() ]);
   const quicTransport =
-      makeQuicTransport(t, [ firstCertificate, secondCertificate ]);
+      makeQuicTransport(t, makeIceTransport(t),
+          [ firstCertificate, secondCertificate ]);
   assert_object_equals(quicTransport.getLocalParameters(), {
     role: 'auto',
-    fingerprints: [ firstCertificate.getFingerprints()[0],
-        secondCertificate.getFingerprints()[0] ],
+    fingerprints:
+        [ firstCertificate.getFingerprints()[0],
+            secondCertificate.getFingerprints()[0] ],
   });
   assert_array_equals(quicTransport.getCertificates(),
       [ firstCertificate, secondCertificate ]);
 }, 'getLocalParameters() has fingerprints for all certificates passed in the ' +
     'constructor.');
 
 promise_test(async t => {
   const expiredCertificate = await generateCertificate({ expires: 0 });
   assert_throws(new TypeError(),
-      () => makeQuicTransport(t, [ expiredCertificate ]));
+      () => makeQuicTransport(t, makeIceTransport(t), [ expiredCertificate ]));
 }, 'RTCQuicTransport constructor throws if passed an expired certificate.');
 
-test(t => {
-  const iceTransport = new RTCIceTransport();
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
   iceTransport.stop();
   assert_throws('InvalidStateError',
-      () => new RTCQuicTransport(iceTransport, []));
+      () => makeQuicTransport(t, iceTransport, [ certificate ]));
 }, 'RTCQuicTransport constructor throws if passed a closed RTCIceTransport.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
+  const firstQuicTransport =
+      makeQuicTransport(t, iceTransport, [ certificate ]);
+  assert_throws('InvalidStateError',
+      () => makeQuicTransport(t, iceTransport, [ certificate ]));
+}, 'RTCQuicTransport constructor throws if passed an RTCIceTransport that ' +
+    'already has an active RTCQuicTransport.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_equals(quicTransport.state, 'closed');
 }, `stop() changes state to 'closed'.`);
 
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.transport.stop();
+  assert_equals(quicTransport.state, 'closed');
+}, `RTCIceTransport.stop() changes RTCQuicTransport.state to 'closed'.`);
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_equals(quicTransport.state, 'new');
+}, 'start() with a non-started RTCIceTransport does not change state.');
+
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const [ localIceTransport, remoteIceTransport ] =
+      makeAndGatherTwoIceTransports(t);
+  const quicTransport =
+      makeQuicTransport(t, localIceTransport, [ certificate ]);
+  quicTransport.start(quicTransport.getLocalParameters());
+  const iceTransportWatcher =
+      new EventWatcher(t, remoteIceTransport, 'icecandidate');
+  await iceTransportWatcher.wait_for('icecandidate');
+  localIceTransport.start(remoteIceTransport.getLocalParameters(),
+      'controlling');
+  assert_equals(quicTransport.state, 'connecting');
+}, 'start() with a non-started RTCIceTransport later changes state to ' +
+    `'connecting' once the RTCIceTransport.start() is called.`);
+
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const [ localIceTransport, remoteIceTransport ] =
+      makeAndGatherTwoIceTransports(t);
+  const quicTransport =
+      makeQuicTransport(t, localIceTransport, [ certificate ]);
+  const iceTransportWatcher =
+      new EventWatcher(t, remoteIceTransport, 'icecandidate');
+  await iceTransportWatcher.wait_for('icecandidate');
+  localIceTransport.start(remoteIceTransport.getLocalParameters());
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_equals(quicTransport.state, 'connecting');
+}, `start() with a started RTCIceTransport changes state to 'connecting'.`);
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.stop();
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called after stop().');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.transport.stop();
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called after the RTCIceTransport has stopped.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called twice.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeAndStartTwoQuicTransports(t);
+  const localWatcher = new EventWatcher(t, localQuicTransport, 'statechange');
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
+  await Promise.all([
+    localWatcher.wait_for('statechange').then(() => {
+      assert_equals(localQuicTransport.state, 'connected');
+    }),
+    remoteWatcher.wait_for('statechange').then(() => {
+      assert_equals(remoteQuicTransport.state, 'connected');
+    }),
+  ]);
+}, 'Two RTCQuicTransports connect to each other.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  localQuicTransport.stop();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
+  await remoteWatcher.wait_for('statechange');
+  assert_equals(remoteQuicTransport.state, 'closed');
+}, `stop() fires a statechange event to 'closed' on the remote transport`);
+
 </script>
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    text-align: center;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>This is a test</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center</title>
+<link rel="match" href="align_center-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_50-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center, position:50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    text-align: center;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>This is a test</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_50.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center, position:50%</title>
+<link rel="match" href="align_center_position_50-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center_position_50.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center, position greater than 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    right: 23px;
+    width: 64px;
+    text-align: center;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>Aweso<br>me!!!</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_gt_50.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center, position greater than 50%</title>
+<link rel="match" href="align_center_position_gt_50-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center_position_gt_50.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center, position less than 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 23px;
+    right: 0;
+    width: 64px;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>Awesome<br>!!!</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center, position less than 50%</title>
+<link rel="match" href="align_center_position_lt_50-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center_position_lt_50.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center, position less than 50%, size greater than maximum size</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 23px;
+    right: 0;
+    width: 64px;
+    text-align: center;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>Aweso<br>me!!!</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_position_lt_50_size_gt_maximum_size.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center, position less than 50%, size greater than maximum size</title>
+<link rel="match" href="align_center_position_lt_50_size_gt_maximum_size-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center_position_lt_50_size_gt_maximum_size.vtt">
+</video>
+<script>
+document.getElementsByTagName('track')[0].track.mode = 'showing';
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, align:center with long cues</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    text-align: center;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class=video><span class=cue><span>This is a test subtitle that <br>most likely will span over <br>several rows since it is a pretty <br>long cue with a lot of text.</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_center_wrapped.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, align:center with long cues</title>
+<link rel="match" href="align_center_wrapped-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="support/align_center_long.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle-ref.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    text-align: center;
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle</title>
-<link rel="match" href="align_middle-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50-ref.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position:50%</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    text-align: center;
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>This is a test</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_50.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position:50%</title>
-<link rel="match" href="align_middle_position_50-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_50.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50-ref.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position greater than 50%</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    right: 23px;
-    width: 64px;
-    text-align: center;
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>Aweso<br>me!!!</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_gt_50.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position greater than 50%</title>
-<link rel="match" href="align_middle_position_gt_50-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_gt_50.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50-ref.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    left: 23px;
-    right: 0;
-    width: 64px;
-    text-align: center
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>Awesome<br>!!!</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position less than 50%</title>
-<link rel="match" href="align_middle_position_lt_50-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_lt_50.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size-ref.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle, position less than 50%, size greater than maximum size</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    left: 23px;
-    right: 0;
-    width: 64px;
-    text-align: center;
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>Aweso<br>me!!!</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_position_lt_50_size_gt_maximum_size.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle, position less than 50%, size greater than maximum size</title>
-<link rel="match" href="align_middle_position_lt_50_size_gt_maximum_size-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_position_lt_50_size_gt_maximum_size.vtt">
-</video>
-<script>
-document.getElementsByTagName('track')[0].track.mode = 'showing';
-</script>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped-ref.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<title>Reference for WebVTT rendering, align:middle with long cues</title>
-<style>
-html { overflow:hidden }
-body { margin:0 }
-.video {
-    display: inline-block;
-    width: 320px;
-    height: 180px;
-    position: relative;
-    font-size: 9px;
-}
-.cue {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    text-align: center;
-}
-.cue > span {
-    font-family: Ahem, sans-serif;
-    background: rgba(0,0,0,0.8);
-    color: green;
-}
-</style>
-<div class=video><span class=cue><span>This is a test subtitle that <br>most likely will span over <br>several rows since it is a pretty <br>long cue with a lot of text.</span></span></div>
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/align_middle_wrapped.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<title>WebVTT rendering, align:middle with long cues</title>
-<link rel="match" href="align_middle_wrapped-ref.html">
-<style>
-html { overflow:hidden }
-body { margin:0 }
-::cue {
-    font-family: Ahem, sans-serif;
-    color: green
-}
-</style>
-<script src="/common/reftest-wait.js"></script>
-<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
-    <source src="/media/white.webm" type="video/webm">
-    <source src="/media/white.mp4" type="video/mp4">
-    <track src="support/align_middle_long.vtt">
-    <script>
-    document.getElementsByTagName('track')[0].track.mode = 'showing';
-    </script>
-</video>
-</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/basic-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, default</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/basic.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, default</title>
+<link rel="match" href="basic-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/basic.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, regionanchor x 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: -50%;
+    right: 50%;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_x_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, regionanchor x 50%</title>
+<link rel="match" href="regionanchor_x_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/regionanchor_x_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, regionanchor y 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+</style>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/regionanchor_y_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, regionanchor y 50%</title>
+<link rel="match" href="regionanchor_y_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/regionanchor_y_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, scroll up</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a second test subtitle<br/>This is a third test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/scroll_up.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, scroll up</title>
+<link rel="match" href="scroll_up-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshotDelayed(3000);">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/scroll_up.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, single line top left</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    text-align: left
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/single_line_top_left.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, single line top left</title>
+<link rel="match" href="single_line_top_left-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/single_line_top_left.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/basic.vtt
@@ -0,0 +1,7 @@
+WEBVTT
+
+REGION
+id:1
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_x_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+regionanchor:50%,100%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/regionanchor_y_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+regionanchor:0%,50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/scroll_up.vtt
@@ -0,0 +1,16 @@
+WEVTT
+
+REGION
+id:1
+lines:2
+regionanchor:0%,0%
+scroll:up
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a first test subtitle
+
+00:00:01.000 --> 00:00:05.000 region:1
+This is a second test subtitle
+
+00:00:01.000 --> 00:00:05.000 region:1
+This is a third test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/single_line_top_left.vtt
@@ -0,0 +1,10 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:0%,0%
+regionanchor:0%,0%
+lines:1
+
+00:00:00.000 --> 00:00:05.000 region:1 align:left
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_x_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:50%,100%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/viewportanchor_y_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+viewportanchor:0%,50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/support/width_50_percent.vtt
@@ -0,0 +1,8 @@
+WEBVTT
+
+REGION
+id:1
+width:50%
+
+00:00:00.000 --> 00:00:05.000 region:1
+This is a test subtitle
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, viewportanchor x 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+    overflow: hidden;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    right: -50%;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_x_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, viewportanchor x 50%</title>
+<link rel="match" href="viewportanchor_x_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/viewportanchor_x_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, viewportanchor y 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 50%;
+    left: 0;
+    right: 0;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/viewportanchor_y_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, viewportanchor y 50%</title>
+<link rel="match" href="viewportanchor_y_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/viewportanchor_y_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering regions, width 50%</title>
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 50%;
+    text-align: center
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>This is a test subtitle</span></span></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/regions/width_50_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering regions, width 50%</title>
+<link rel="match" href="width_50_percent-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    font-family: Ahem, sans-serif;
+    color: green
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="../media/white.webm" type="video/webm">
+    <source src="../media/white.mp4" type="video/mp4">
+    <track src="support/width_50_percent.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center
+This is a test
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center_long.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center
+This is a test subtitle that most likely will span over several rows since it is a pretty long cue with a lot of text.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:50%
+This is a test
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_gt_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:90%
+Awesome!!!
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:10%
+Awesome!!!
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_center_position_lt_50_size_gt_maximum_size.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 align:center position:10% size:75%
+Awesome!!!
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle
-This is a test
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_long.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle
-This is a test subtitle that most likely will span over several rows since it is a pretty long cue with a lot of text.
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:50%
-This is a test
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_gt_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:90%
-Awesome!!!
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:10%
-Awesome!!!
deleted file mode 100644
--- a/testing/web-platform/tests/webvtt/rendering/cues-with-video/processing-model/support/align_middle_position_lt_50_size_gt_maximum_size.vtt
+++ /dev/null
@@ -1,4 +0,0 @@
-WEBVTT
-
-00:00:00.000 --> 00:00:05.000 align:middle position:10% size:75%
-Awesome!!!
--- a/testing/web-platform/tests/workers/modules/dedicated-worker-import-blob-url.any.js
+++ b/testing/web-platform/tests/workers/modules/dedicated-worker-import-blob-url.any.js
@@ -4,18 +4,18 @@
 // and waits until the list of imported modules is sent from the worker. Passes
 // if the list is equal to |testCase.expectation|.
 function import_blob_url_test(testCase) {
   promise_test(async () => {
     const importURL = new URL(testCase.scriptURL, location.href);
     const blob = new Blob([`import "${importURL}";`],
                           { type: 'text/javascript' });
     const blobURL = URL.createObjectURL(blob);
-
     const worker = new Worker(blobURL, { type: 'module'});
+    worker.postMessage('Send message for tests from main script.');
     const msgEvent = await new Promise((resolve, reject) => {
       worker.onmessage = resolve;
       worker.onerror = (error) => reject(error && error.message);
     });
     assert_array_equals(msgEvent.data, testCase.expectation);
   }, testCase.description);
 }
 
--- a/testing/web-platform/tests/workers/modules/dedicated-worker-import-data-url.any.js
+++ b/testing/web-platform/tests/workers/modules/dedicated-worker-import-data-url.any.js
@@ -8,14 +8,15 @@ function import_data_url_test(testCase) 
     // The Access-Control-Allow-Origin header is necessary because a worker
     // loaded from a data URL has a null origin and import() on the worker
     // without the header is blocked.
     const importURL = new URL(testCase.scriptURL, location.href) +
         '?pipe=header(Access-Control-Allow-Origin, *)';
     const dataURL = `data:text/javascript,import "${importURL}";`;
 
     const worker = new Worker(dataURL, { type: 'module'});
+    worker.postMessage('Send message for tests from main script.');
     const msgEvent = await new Promise(resolve => worker.onmessage = resolve);
     assert_array_equals(msgEvent.data, testCase.expectation);
   }, testCase.description);
 }
 
 testCases.forEach(import_data_url_test);
--- a/testing/web-platform/tests/workers/modules/dedicated-worker-import.any.js
+++ b/testing/web-platform/tests/workers/modules/dedicated-worker-import.any.js
@@ -1,16 +1,17 @@
 // META: script=/workers/modules/resources/import-test-cases.js
 
 // Starts a dedicated worker for |testCase.scriptURL| and waits until the list
 // of imported modules is sent from the worker. Passes if the list is equal to
 // |testCase.expectation|.
 function import_test(testCase) {
   promise_test(async () => {
     const worker = new Worker(testCase.scriptURL, { type: 'module' });
+    worker.postMessage('Send message for tests from main script.');
     const msgEvent = await new Promise((resolve, reject) => {
       worker.onmessage = resolve;
       worker.onerror = (error) => reject(error && error.message);
     });
     assert_array_equals(msgEvent.data, testCase.expectation);
   }, testCase.description);
 }
 
--- a/testing/web-platform/tests/workers/modules/resources/dynamic-import-and-then-static-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/dynamic-import-and-then-static-import-worker.js
@@ -1,2 +1,20 @@
-import('./export-on-static-import-script.js')
-  .then(module => postMessage(module.importedModules));
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
+const sourcePromise = new Promise(resolve => {
+  self.onmessage = e => {
+    // DedicatedWorkerGlobalScope doesn't fill in e.source,
+    // so use e.target instead.
+    const source = e.source ? e.source : e.target;
+    resolve(source);
+  };
+});
+
+const importedModulesPromise =
+  import('./export-on-static-import-script.js')
+    .then(module => module.importedModules)
+    .catch(error => `Failed to do dynamic import: ${error}`);
+
+Promise.all([sourcePromise, importedModulesPromise]).then(results => {
+  const [source, importedModules] = results;
+  source.postMessage(importedModules);
+});
--- a/testing/web-platform/tests/workers/modules/resources/dynamic-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/dynamic-import-worker.js
@@ -1,2 +1,20 @@
-import('./export-on-load-script.js')
-  .then(module => postMessage(module.importedModules));
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
+const sourcePromise = new Promise(resolve => {
+  self.onmessage = e => {
+    // DedicatedWorkerGlobalScope doesn't fill in e.source,
+    // so use e.target instead.
+    const source = e.source ? e.source : e.target;
+    resolve(source);
+  };
+});
+
+const importedModulesPromise =
+  import('./export-on-load-script.js')
+    .then(module => module.importedModules)
+    .catch(error => `Failed to do dynamic import: ${error}`);
+
+Promise.all([sourcePromise, importedModulesPromise]).then(results => {
+  const [source, importedModules] = results;
+  source.postMessage(importedModules);
+});
--- a/testing/web-platform/tests/workers/modules/resources/eval-dynamic-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/eval-dynamic-import-worker.js
@@ -1,3 +1,18 @@
-const code = "import('./export-on-load-script.js')" +
-             "  .then(module => postMessage(module.importedModules));"
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
+const code =
+  "const sourcePromise = new Promise(resolve => {" +
+  "  self.onmessage = e => {" +
+  "    const source = e.source ? e.source : e.target;" +
+  "    resolve(source);" +
+  "  };" +
+  "});" +
+  "const importedModulesPromise =" +
+  "  import('./export-on-load-script.js')" +
+  "    .then(module => module.importedModules)" +
+  "    .catch(error => `Failed to do dynamic import: ${error}`);" +
+  "Promise.all([sourcePromise, importedModulesPromise]).then(results => {" +
+  "  const [source, importedModules] = results;" +
+  "  source.postMessage(importedModules);" +
+  "});";
 eval(code);
--- a/testing/web-platform/tests/workers/modules/resources/export-on-dynamic-import-script.js
+++ b/testing/web-platform/tests/workers/modules/resources/export-on-dynamic-import-script.js
@@ -1,7 +1,8 @@
 // Export the list of imported modules. It's available after the |ready| promise
 // is resolved.
 export let importedModules = ['export-on-dynamic-import-script.js'];
 export let ready = import('./export-on-load-script.js')
   .then(module => {
-      Array.prototype.push.apply(importedModules, module.importedModules);
+    Array.prototype.push.apply(importedModules, module.importedModules);
+    return importedModules;
   });
--- a/testing/web-platform/tests/workers/modules/resources/import-test-cases.js
+++ b/testing/web-platform/tests/workers/modules/resources/import-test-cases.js
@@ -1,49 +1,49 @@
 const testCases = [
     {
-        scriptURL: 'resources/static-import-worker.js',
+        scriptURL: '/workers/modules/resources/static-import-worker.js',
         expectation: ['export-on-load-script.js'],
         description: 'Static import.'
     },
     {
-        scriptURL: 'resources/nested-static-import-worker.js',
+        scriptURL: '/workers/modules/resources/nested-static-import-worker.js',
         expectation: [
             'export-on-static-import-script.js',
             'export-on-load-script.js'
         ],
         description: 'Nested static import.'
     },
     {
-        scriptURL: 'resources/static-import-and-then-dynamic-import-worker.js',
+        scriptURL: '/workers/modules/resources/static-import-and-then-dynamic-import-worker.js',
         expectation: [
             'export-on-dynamic-import-script.js',
             'export-on-load-script.js'
         ],
         description: 'Static import and then dynamic import.'
     },
     {
-        scriptURL: 'resources/dynamic-import-worker.js',
+        scriptURL: '/workers/modules/resources/dynamic-import-worker.js',
         expectation: ['export-on-load-script.js'],
         description: 'Dynamic import.'
     },
     {
-        scriptURL: 'resources/nested-dynamic-import-worker.js',
+        scriptURL: '/workers/modules/resources/nested-dynamic-import-worker.js',
         expectation: [
             'export-on-dynamic-import-script.js',
             'export-on-load-script.js'
         ],
         description: 'Nested dynamic import.'
     },
     {
-        scriptURL: 'resources/dynamic-import-and-then-static-import-worker.js',
+        scriptURL: '/workers/modules/resources/dynamic-import-and-then-static-import-worker.js',
         expectation: [
             'export-on-static-import-script.js',
             'export-on-load-script.js'
         ],
         description: 'Dynamic import and then static import.'
     },
     {
-        scriptURL: 'resources/eval-dynamic-import-worker.js',
+        scriptURL: '/workers/modules/resources/eval-dynamic-import-worker.js',
         expectation: ['export-on-load-script.js'],
         description: 'eval(import()).'
     }
 ];
--- a/testing/web-platform/tests/workers/modules/resources/nested-dynamic-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/nested-dynamic-import-worker.js
@@ -1,5 +1,22 @@
-import('./export-on-dynamic-import-script.js')
-  .then(async module => {
-    await module.ready;
-    postMessage(module.importedModules);
-  });
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
+
+const sourcePromise = new Promise(resolve => {
+  self.onmessage = e => {
+    // DedicatedWorkerGlobalScope doesn't fill in e.source,
+    // so use e.target instead.
+    const source = e.source ? e.source : e.target;
+    resolve(source);
+  };
+});
+
+const importedModulesPromise =
+  import('./export-on-dynamic-import-script.js')
+    .then(module => module.ready)
+    .then(importedModules => importedModules)
+    .catch(error => `Failed to do dynamic import: ${error}`);
+
+Promise.all([sourcePromise, importedModulesPromise]).then(results => {
+  const [source, importedModules] = results;
+  source.postMessage(importedModules);
+});
--- a/testing/web-platform/tests/workers/modules/resources/nested-static-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/nested-static-import-worker.js
@@ -1,2 +1,9 @@
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
 import * as module from './export-on-static-import-script.js';
-postMessage(module.importedModules);
+self.onmessage = e => {
+  // DedicatedWorkerGlobalScope doesn't fill in e.source,
+  // so use e.target instead.
+  const source = e.source ? e.source : e.target;
+  source.postMessage(module.importedModules);
+};
--- a/testing/web-platform/tests/workers/modules/resources/static-import-and-then-dynamic-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/static-import-and-then-dynamic-import-worker.js
@@ -1,2 +1,22 @@
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
 import * as module from './export-on-dynamic-import-script.js';
-module.ready.then(() => postMessage(module.importedModules));
+
+const sourcePromise = new Promise(resolve => {
+  self.onmessage = e => {
+    // DedicatedWorkerGlobalScope doesn't fill in e.source,
+    // so use e.target instead.
+    const source = e.source ? e.source : e.target;
+    resolve(source);
+  };
+});
+
+export let importedModules = ['export-on-dynamic-import-script.js'];
+const importedModulesPromise = module.ready
+  .then(importedModules => importedModules)
+  .catch(error => `Failed to do dynamic import: ${error}`);
+
+Promise.all([sourcePromise, importedModulesPromise]).then(results => {
+  const [source, importedModules] = results;
+  source.postMessage(importedModules);
+});
--- a/testing/web-platform/tests/workers/modules/resources/static-import-worker.js
+++ b/testing/web-platform/tests/workers/modules/resources/static-import-worker.js
@@ -1,2 +1,9 @@
+// This script is meant to be imported by a module worker. It receives a
+// message from the worker and responds with the list of imported modules.
 import * as module from './export-on-load-script.js';
-postMessage(module.importedModules);
+self.onmessage = e => {
+  // DedicatedWorkerGlobalScope doesn't fill in e.source,
+  // so use e.target instead.
+  const source = e.source ? e.source : e.target;
+  source.postMessage(module.importedModules);
+};
--- a/testing/web-platform/tests/xhr/overridemimetype-edge-cases.window.js
+++ b/testing/web-platform/tests/xhr/overridemimetype-edge-cases.window.js
@@ -1,28 +1,38 @@
 const testURL = "resources/status.py?type=" + encodeURIComponent("text/plain;charset=windows-1252") + "&content=%C2%F0";
 
 async_test(t => {
   const client = new XMLHttpRequest();
+  client.onload = t.step_func_done(() => {
+    assert_equals(client.responseText, "\uFFFD\uFFFD");
+  });
+  client.overrideMimeType("text/plain;charset=UTF-8");
+  client.open("GET", testURL);
+  client.send();
+}, "overrideMimeType() is not reset by open(), basic");
+
+async_test(t => {
+  const client = new XMLHttpRequest();
   let secondTime = false;
   client.onload = t.step_func(() => {
     if(!secondTime) {
       assert_equals(client.responseText, "\uFFFD\uFFFD");
       secondTime = true;
       client.open("GET", testURL);
       client.send();
     } else {
-      assert_equals(client.responseText, "Âð");
+      assert_equals(client.responseText, "\uFFFD\uFFFD");
       t.done();
     }
   });
   client.open("GET", testURL);
   client.overrideMimeType("text/plain;charset=UTF-8")
   client.send();
-}, "overrideMimeType() state needs to be reset across requests");
+}, "overrideMimeType() is not reset by open()");
 
 async_test(t => {
   const client = new XMLHttpRequest();
   client.onload = t.step_func_done(() => {
     assert_equals(client.responseText, "Âð")
   });
   client.open("GET", testURL);
   client.overrideMimeType("text/xml");
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js