Merge mozilla-central to autoland
authorDorel Luca <dluca@mozilla.com>
Mon, 04 Jun 2018 21:14:46 +0300
changeset 804106 799bbe3cec7d3baa85fe3889c87d4a8ca185e703
parent 804105 d2202116d877960d828b06d43705186d5f3d31cd (current diff)
parent 803743 d8f180ab74921fd07a66d6868914a48e5f9ea797 (diff)
child 804107 81b41583c253f469a50d88de9b07c6ca22f9d140
push id112312
push userbmo:standard8@mozilla.com
push dateTue, 05 Jun 2018 16:07:56 +0000
milestone62.0a1
Merge mozilla-central to autoland
browser/base/content/blockedSite.js
dom/animation/test/css-animations/test_animation-cancel.html
dom/animation/test/css-animations/test_animation-computed-timing.html
dom/animation/test/css-animations/test_animation-currenttime.html
dom/animation/test/css-animations/test_animation-finish.html
dom/animation/test/css-animations/test_animation-finished.html
dom/animation/test/css-animations/test_animation-id.html
dom/animation/test/css-animations/test_animation-pausing.html
dom/animation/test/css-animations/test_animation-playstate.html
dom/animation/test/css-animations/test_animation-ready.html
dom/animation/test/css-animations/test_animation-reverse.html
dom/animation/test/css-animations/test_animation-starttime.html
dom/animation/test/css-animations/test_animations-dynamic-changes.html
dom/animation/test/css-animations/test_cssanimation-animationname.html
dom/animation/test/css-animations/test_document-get-animations.html
dom/animation/test/css-animations/test_effect-target.html
dom/animation/test/css-animations/test_element-get-animations.html
dom/animation/test/css-animations/test_event-dispatch.html
dom/animation/test/css-animations/test_event-order.html
dom/animation/test/css-animations/test_keyframeeffect-getkeyframes.html
dom/animation/test/css-animations/test_pseudoElement-get-animations.html
dom/animation/test/css-animations/test_setting-effect.html
testing/web-platform/meta/MANIFEST.json
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1236,16 +1236,22 @@ HyperTextAccessible::TextBounds(int32_t 
   index_t startOffset = ConvertMagicOffset(aStartOffset);
   index_t endOffset = ConvertMagicOffset(aEndOffset);
   if (!startOffset.IsValid() || !endOffset.IsValid() ||
       startOffset > endOffset || endOffset > CharacterCount()) {
     NS_ERROR("Wrong in offset");
     return nsIntRect();
   }
 
+  if (CharacterCount() == 0) {
+    nsPresContext* presContext = mDoc->PresContext();
+    // Empty content, use our own bound to at least get x,y coordinates
+    return GetFrame()->GetScreenRectInAppUnits().
+      ToNearestPixels(presContext->AppUnitsPerDevPixel());
+  }
 
   int32_t childIdx = GetChildIndexAtOffset(startOffset);
   if (childIdx == -1)
     return nsIntRect();
 
   nsIntRect bounds;
   int32_t prevOffset = GetChildOffset(childIdx);
   int32_t offset1 = startOffset - prevOffset;
--- a/accessible/tests/browser/bounds/browser_test_zoom_text.js
+++ b/accessible/tests/browser/bounds/browser_test_zoom_text.js
@@ -14,20 +14,31 @@ async function runTests(browser, accDoc)
     let textNode = hyperTextNode.firstChild;
 
     let contentDPR = await getContentDPR(browser);
     let [x, y, width, height] = getBounds(textNode, contentDPR);
     testTextBounds(hyperTextNode, 0, -1, [x, y, width, height],
                    COORDTYPE_SCREEN_RELATIVE);
   }
 
+  async function testEmptyInputNode(id) {
+    let inputNode = findAccessibleChildByID(accDoc, id);
+
+    let [x, y, width, height] = getBounds(inputNode);
+    testTextBounds(inputNode, 0, -1, [x, y, width, height],
+                   COORDTYPE_SCREEN_RELATIVE);
+    testTextBounds(inputNode, 0, 0, [x, y, width, height],
+                   COORDTYPE_SCREEN_RELATIVE);
+  }
+
   loadFrameScripts(browser, { name: "layout.js", dir: MOCHITESTS_DIR });
 
   await testTextNode("p1");
   await testTextNode("p2");
+  await testEmptyInputNode("i1");
 
   await ContentTask.spawn(browser, {}, () => {
     zoomDocument(document, 2.0);
   });
 
   await testTextNode("p1");
 
   await ContentTask.spawn(browser, {}, () => {
@@ -35,10 +46,11 @@ async function runTests(browser, accDoc)
   });
 }
 
 /**
  * Test the text range boundary when page is zoomed
  */
 addAccessibleTask(`
   <p id='p1' style='font-family: monospace;'>Tilimilitryamdiya</p>
-  <p id='p2'>ل</p>`, runTests
+  <p id='p2'>ل</p>
+  <form><input id='i1' /></form>`, runTests
 );
deleted file mode 100644
--- a/browser/base/content/blockedSite.js
+++ /dev/null
@@ -1,155 +0,0 @@
-// Error url MUST be formatted like this:
-// about:blocked?e=error_code&u=url(&o=1)?
-// (o=1 when user overrides are allowed)
-
-// 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 getURL() {
-  var url = document.documentURI;
-  var match = url.match(/&u=([^&]+)&/);
-
-  // match == null if not found; if so, return an empty string
-  // instead of what would turn out to be portions of the URI
-  if (!match)
-    return "";
-
-  url = decodeURIComponent(match[1]);
-
-  // If this is a view-source page, then get then real URI of the page
-  if (url.startsWith("view-source:"))
-    url = url.slice(12);
-  return url;
-}
-
-/**
- * Check whether this warning page is overridable or not, in which case
- * the "ignore the risk" suggestion in the error description
- * should not be shown.
- */
-function getOverride() {
-  var url = document.documentURI;
-  var match = url.match(/&o=1&/);
-  return !!match;
-}
-
-/**
- * Attempt to get the hostname via document.location.  Fail back
- * to getURL so that we always return something meaningful.
- */
-function getHostString() {
-  try {
-    return document.location.hostname;
-  } catch (e) {
-    return getURL();
-  }
-}
-
-function onClickSeeDetails() {
-  let details = document.getElementById("errorDescriptionContainer");
-  if (details.hidden) {
-    details.removeAttribute("hidden");
-  } else {
-    details.setAttribute("hidden", "true");
-  }
-}
-
-function initPage() {
-  var error = "";
-  switch (getErrorCode()) {
-    case "malwareBlocked" :
-      error = "malware";
-      break;
-    case "deceptiveBlocked" :
-      error = "phishing";
-      break;
-    case "unwantedBlocked" :
-      error = "unwanted";
-      break;
-    case "harmfulBlocked" :
-      error = "harmful";
-      break;
-    default:
-      error = "harmful";
-      return;
-  }
-
-  var el;
-
-  if (error !== "malware") {
-    el = document.getElementById("errorTitleText_malware");
-    el.remove();
-    el = document.getElementById("errorShortDescText_malware");
-    el.remove();
-    el = document.getElementById("errorLongDesc_malware");
-    el.remove();
-  }
-
-  if (error !== "phishing") {
-    el = document.getElementById("errorTitleText_phishing");
-    el.remove();
-    el = document.getElementById("errorShortDescText_phishing");
-    el.remove();
-    el = document.getElementById("errorLongDesc_phishing");
-    el.remove();
-  }
-
-  if (error !== "unwanted") {
-    el = document.getElementById("errorTitleText_unwanted");
-    el.remove();
-    el = document.getElementById("errorShortDescText_unwanted");
-    el.remove();
-    el = document.getElementById("errorLongDesc_unwanted");
-    el.remove();
-  }
-
-  if (error !== "harmful") {
-    el = document.getElementById("errorTitleText_harmful");
-    el.remove();
-    el = document.getElementById("errorShortDescText_harmful");
-    el.remove();
-    el = document.getElementById("errorLongDesc_harmful");
-    el.remove();
-  }
-
-  // Decide which version of the string should be visible in the error description.
-  if (getOverride()) {
-    document.getElementById(error + "_error_desc_no_override").remove();
-  } else {
-    document.getElementById(error + "_error_desc_override").remove();
-  }
-
-  // Set sitename in error details.
-  let sitenameElem = document.getElementById(error + "_sitename");
-  sitenameElem.setAttribute("class", "sitename");
-  sitenameElem.textContent = getHostString();
-
-  document.title = document.getElementById("errorTitleText_" + error).textContent;
-
-  // Inform the test harness that we're done loading the page.
-  var event = new CustomEvent("AboutBlockedLoaded",
-    {
-      bubbles: true,
-      detail: {
-        url: this.getURL(),
-        err: error
-      }
-    });
-  document.dispatchEvent(event);
-}
-
-document.getElementById("seeDetailsButton").onclick = onClickSeeDetails();
-// 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/browser/base/content/blockedSite.xhtml
+++ b/browser/base/content/blockedSite.xhtml
@@ -12,19 +12,169 @@
 ]>
 
 <!-- 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" class="blacklist">
   <head>
-    <meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
     <link rel="stylesheet" href="chrome://browser/skin/blockedSite.css" type="text/css" media="all" />
     <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>
+
+    <script type="application/javascript"><![CDATA[
+      // Error url MUST be formatted like this:
+      //   about:blocked?e=error_code&u=url(&o=1)?
+      //     (o=1 when user overrides are allowed)
+
+      // 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 getURL() {
+        var url = document.documentURI;
+        var match = url.match(/&u=([^&]+)&/);
+
+        // match == null if not found; if so, return an empty string
+        // instead of what would turn out to be portions of the URI
+        if (!match)
+          return "";
+
+        url = decodeURIComponent(match[1]);
+
+        // If this is a view-source page, then get then real URI of the page
+        if (url.startsWith("view-source:"))
+          url = url.slice(12);
+        return url;
+      }
+
+      /**
+       * Check whether this warning page is overridable or not, in which case
+       * the "ignore the risk" suggestion in the error description
+       * should not be shown.
+       */
+      function getOverride() {
+        var url = document.documentURI;
+        var match = url.match(/&o=1&/);
+        return !!match;
+      }
+
+      /**
+       * Attempt to get the hostname via document.location.  Fail back
+       * to getURL so that we always return something meaningful.
+       */
+      function getHostString() {
+        try {
+          return document.location.hostname;
+        } catch (e) {
+          return getURL();
+        }
+      }
+
+      function onClickSeeDetails() {
+        let details = document.getElementById("errorDescriptionContainer");
+        if (details.hidden) {
+          details.removeAttribute("hidden");
+        } else {
+          details.setAttribute("hidden", "true");
+        }
+      }
+
+      function initPage() {
+        var error = "";
+        switch (getErrorCode()) {
+          case "malwareBlocked" :
+            error = "malware";
+            break;
+          case "deceptiveBlocked" :
+            error = "phishing";
+            break;
+          case "unwantedBlocked" :
+            error = "unwanted";
+            break;
+          case "harmfulBlocked" :
+            error = "harmful";
+            break;
+          default:
+            return;
+        }
+
+        var el;
+
+        if (error !== "malware") {
+          el = document.getElementById("errorTitleText_malware");
+          el.remove();
+          el = document.getElementById("errorShortDescText_malware");
+          el.remove();
+          el = document.getElementById("errorLongDesc_malware");
+          el.remove();
+        }
+
+        if (error !== "phishing") {
+          el = document.getElementById("errorTitleText_phishing");
+          el.remove();
+          el = document.getElementById("errorShortDescText_phishing");
+          el.remove();
+          el = document.getElementById("errorLongDesc_phishing");
+          el.remove();
+        }
+
+        if (error !== "unwanted") {
+          el = document.getElementById("errorTitleText_unwanted");
+          el.remove();
+          el = document.getElementById("errorShortDescText_unwanted");
+          el.remove();
+          el = document.getElementById("errorLongDesc_unwanted");
+          el.remove();
+        }
+
+        if (error !== "harmful") {
+          el = document.getElementById("errorTitleText_harmful");
+          el.remove();
+          el = document.getElementById("errorShortDescText_harmful");
+          el.remove();
+          el = document.getElementById("errorLongDesc_harmful");
+          el.remove();
+        }
+
+        // Decide which version of the string should be visible in the error description.
+        if (getOverride()) {
+          document.getElementById(error + "_error_desc_no_override").remove();
+        } else {
+          document.getElementById(error + "_error_desc_override").remove();
+        }
+
+        // Set sitename in error details.
+        let sitenameElem = document.getElementById(error + "_sitename");
+        sitenameElem.setAttribute("class", "sitename");
+        sitenameElem.textContent = getHostString();
+
+        document.title = document.getElementById("errorTitleText_" + error).textContent;
+
+        // Inform the test harness that we're done loading the page.
+        var event = new CustomEvent("AboutBlockedLoaded",
+          {
+            bubbles: true,
+            detail: {
+              url: this.getURL(),
+              err: error
+            }
+          });
+        document.dispatchEvent(event);
+      }
+    ]]></script>
   </head>
 
   <body dir="&locale.dir;">
     <div id="errorPageContainer" class="container">
 
       <!-- Error Title -->
       <div id="errorTitle" class="title">
         <h1 class="title-text" id="errorTitleText_phishing">&safeb.blocked.phishingPage.title3;</h1>
@@ -47,17 +197,17 @@
         <div id="advisoryDesc">
           <p id="advisoryDescText">&safeb.palm.advisory.desc2;</p>
         </div>
 
         <!-- Action buttons -->
         <div id="buttons" class="button-container">
           <!-- Commands handled in browser.js -->
           <button id="goBackButton">&safeb.palm.accept.label2;</button>
-          <button id="seeDetailsButton">&safeb.palm.seedetails.label;</button>
+          <button id="seeDetailsButton" onclick="onClickSeeDetails();">&safeb.palm.seedetails.label;</button>
         </div>
       </div>
       <div id="errorDescriptionContainer" hidden="true">
         <div class="error-description" id="errorLongDesc_phishing">
           <p id="phishing_error_desc_override">&safeb.blocked.phishingPage.errorDesc.override;</p>
           <p id="phishing_error_desc_no_override">&safeb.blocked.phishingPage.errorDesc.noOverride;</p>
           <p id="phishing_learn_more">&safeb.blocked.phishingPage.learnMore;</p>
         </div>
@@ -73,11 +223,18 @@
         </div>
         <div class="error-description" id="errorLongDesc_harmful">
           <p id="harmful_error_desc_override">&safeb.blocked.harmfulPage.errorDesc.override;</p>
           <p id="harmful_error_desc_no_override">&safeb.blocked.harmfulPage.errorDesc.noOverride;</p>
           <p id="harmful_learn_more">&safeb.blocked.harmfulPage.learnMore;</p>
         </div>
       </div>
     </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>
-  <script type="application/javascript" src="chrome://browser/content/blockedSite.js"/>
 </html>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -105,17 +105,16 @@ browser.jar:
 #ifndef XP_MACOSX
 *       content/browser/webrtcIndicator.xul           (content/webrtcIndicator.xul)
         content/browser/webrtcIndicator.js            (content/webrtcIndicator.js)
 #endif
 # the following files are browser-specific overrides
 *       content/browser/license.html                  (/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://browser/content/license.html
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
-        content/browser/blockedSite.js                  (content/blockedSite.js)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
 
 # L10n resources and overrides.
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
 % resource search-plugins chrome://browser/locale/searchplugins/
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.517
+Current extension version is: 2.0.536
 
-Taken from upstream commit: 7cd6c0fb
+Taken from upstream commit: 5053d02b
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -1615,23 +1615,19 @@ module.exports = typeof window !== 'unde
 
 /***/ }),
 /* 3 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
 let compatibilityParams = Object.create(null);
 ;
-const apiCompatibilityParams = Object.freeze(compatibilityParams);
-exports.apiCompatibilityParams = apiCompatibilityParams;
+exports.apiCompatibilityParams = Object.freeze(compatibilityParams);
 
 /***/ }),
 /* 4 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
@@ -1645,18 +1641,18 @@ exports.GlobalWorkerOptions = GlobalWork
 
 /***/ }),
 /* 5 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.517';
-var pdfjsBuild = '7cd6c0fb';
+var pdfjsVersion = '2.0.536';
+var pdfjsBuild = '5053d02b';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayAPI = __w_pdfjs_require__(9);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(17);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(18);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(19);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(4);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(3);
@@ -4839,26 +4835,26 @@ function getDocument(src) {
     }
     params[key] = source[key];
   }
   params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
   params.ignoreErrors = params.stopAtErrors !== true;
   params.pdfBug = params.pdfBug === true;
   const NativeImageDecoderValues = Object.values(_util.NativeImageDecoding);
   if (params.nativeImageDecoderSupport === undefined || !NativeImageDecoderValues.includes(params.nativeImageDecoderSupport)) {
-    params.nativeImageDecoderSupport = _util.NativeImageDecoding.DECODE;
+    params.nativeImageDecoderSupport = _api_compatibility.apiCompatibilityParams.nativeImageDecoderSupport || _util.NativeImageDecoding.DECODE;
   }
   if (!Number.isInteger(params.maxImageSize)) {
     params.maxImageSize = -1;
   }
   if (typeof params.isEvalSupported !== 'boolean') {
     params.isEvalSupported = true;
   }
   if (typeof params.disableFontFace !== 'boolean') {
-    params.disableFontFace = false;
+    params.disableFontFace = _api_compatibility.apiCompatibilityParams.disableFontFace || false;
   }
   if (typeof params.disableRange !== 'boolean') {
     params.disableRange = _api_compatibility.apiCompatibilityParams.disableRange || false;
   }
   if (typeof params.disableStream !== 'boolean') {
     params.disableStream = _api_compatibility.apiCompatibilityParams.disableStream || false;
   }
   if (typeof params.disableAutoFetch !== 'boolean') {
@@ -4924,17 +4920,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.517',
+    apiVersion: '2.0.536',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5746,17 +5742,21 @@ var WorkerTransport = function WorkerTra
         loadingTask._capability.resolve(pdfDocument);
       }, this);
       messageHandler.on('PasswordRequest', function transportPasswordRequest(exception) {
         this._passwordCapability = (0, _util.createPromiseCapability)();
         if (loadingTask.onPassword) {
           var updatePassword = password => {
             this._passwordCapability.resolve({ password });
           };
-          loadingTask.onPassword(updatePassword, exception.code);
+          try {
+            loadingTask.onPassword(updatePassword, exception.code);
+          } catch (ex) {
+            this._passwordCapability.reject(ex);
+          }
         } else {
           this._passwordCapability.reject(new _util.PasswordException(exception.message, exception.code));
         }
         return this._passwordCapability.promise;
       }, this);
       messageHandler.on('PasswordException', function transportPasswordException(exception) {
         loadingTask._capability.reject(new _util.PasswordException(exception.message, exception.code));
       }, this);
@@ -6049,17 +6049,19 @@ var WorkerTransport = function WorkerTra
       });
     },
     get loadingParams() {
       let params = this._params;
       return (0, _util.shadow)(this, 'loadingParams', {
         disableRange: params.disableRange,
         disableStream: params.disableStream,
         disableAutoFetch: params.disableAutoFetch,
-        disableCreateObjectURL: params.disableCreateObjectURL
+        disableCreateObjectURL: params.disableCreateObjectURL,
+        disableFontFace: params.disableFontFace,
+        nativeImageDecoderSupport: params.nativeImageDecoderSupport
       });
     }
   };
   return WorkerTransport;
 }();
 var PDFObjects = function PDFObjectsClosure() {
   function PDFObjects() {
     this.objs = Object.create(null);
@@ -6247,18 +6249,18 @@ var InternalRenderTask = function Intern
         }
       }
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.517';
-  exports.build = build = '7cd6c0fb';
+  exports.version = version = '2.0.536';
+  exports.build = build = '5053d02b';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -20235,17 +20235,17 @@ var PDFFunction = function PDFFunctionCl
       var range = toNumberArray(dict.getArray('Range'));
       if (!domain || !range) {
         throw new _util.FormatError('No domain or range');
       }
       var inputSize = domain.length / 2;
       var outputSize = range.length / 2;
       domain = toMultiArray(domain);
       range = toMultiArray(range);
-      var size = toNumberArray(dict.get('Size'));
+      var size = toNumberArray(dict.getArray('Size'));
       var bps = dict.get('BitsPerSample');
       var order = dict.get('Order') || 1;
       if (order !== 1) {
         (0, _util.info)('No support for cubic spline interpolation: ' + order);
       }
       var encode = toNumberArray(dict.getArray('Encode'));
       if (!encode) {
         encode = [];
@@ -21098,18 +21098,18 @@ exports.PostScriptCompiler = PostScriptC
 
 /***/ }),
 /* 19 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.517';
-var pdfjsBuild = '7cd6c0fb';
+var pdfjsVersion = '2.0.536';
+var pdfjsBuild = '5053d02b';
 var pdfjsCoreWorker = __w_pdfjs_require__(20);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 20 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -21300,17 +21300,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.517';
+    let workerVersion = '2.0.536';
     if (apiVersion !== null && apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _util.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -21455,19 +21455,19 @@ var WorkerMessageHandler = {
         ensureNotTerminated();
         if (e instanceof _util.PasswordException) {
           var task = new WorkerTask('PasswordException: response ' + e.code);
           startWorkerTask(task);
           handler.sendWithPromise('PasswordRequest', e).then(function (data) {
             finishWorkerTask(task);
             pdfManager.updatePassword(data.password);
             pdfManagerReady();
-          }).catch(function (ex) {
+          }).catch(function (boundException) {
             finishWorkerTask(task);
-            handler.send('PasswordException', ex);
+            handler.send('PasswordException', boundException);
           }.bind(null, e));
         } else if (e instanceof _util.InvalidPDFException) {
           handler.send('InvalidPDF', e);
         } else if (e instanceof _util.MissingPDFException) {
           handler.send('MissingPDF', e);
         } else if (e instanceof _util.UnexpectedResponseException) {
           handler.send('UnexpectedResponse', e);
         } else {
@@ -27363,16 +27363,25 @@ let DNLMarkerError = function DNLMarkerE
     this.message = message;
     this.scanLines = scanLines;
   }
   DNLMarkerError.prototype = new Error();
   DNLMarkerError.prototype.name = 'DNLMarkerError';
   DNLMarkerError.constructor = DNLMarkerError;
   return DNLMarkerError;
 }();
+let EOIMarkerError = function EOIMarkerErrorClosure() {
+  function EOIMarkerError(message) {
+    this.message = message;
+  }
+  EOIMarkerError.prototype = new Error();
+  EOIMarkerError.prototype.name = 'EOIMarkerError';
+  EOIMarkerError.constructor = EOIMarkerError;
+  return EOIMarkerError;
+}();
 var JpegImage = function JpegImageClosure() {
   var dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
   var dctCos1 = 4017;
   var dctSin1 = 799;
   var dctCos3 = 3406;
   var dctSin3 = 2276;
   var dctCos6 = 1567;
   var dctSin6 = 3784;
@@ -27446,16 +27455,18 @@ var JpegImage = function JpegImageClosur
         var nextByte = data[offset++];
         if (nextByte) {
           if (nextByte === 0xDC && parseDNLMarker) {
             offset += 2;
             const scanLines = data[offset++] << 8 | data[offset++];
             if (scanLines > 0 && scanLines !== frame.scanLines) {
               throw new DNLMarkerError('Found DNL marker (0xFFDC) while parsing scan data', scanLines);
             }
+          } else if (nextByte === 0xD9) {
+            throw new EOIMarkerError('Found EOI marker (0xFFD9) while parsing scan data');
           }
           throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
         }
       }
       bitsCount = 7;
       return bitsData >>> 7;
     }
     function decodeHuffman(tree) {
@@ -27932,17 +27943,17 @@ var JpegImage = function JpegImageClosur
       var quantizationTables = [];
       var huffmanTablesAC = [],
           huffmanTablesDC = [];
       var fileMarker = readUint16();
       if (fileMarker !== 0xFFD8) {
         throw new JpegError('SOI not found');
       }
       fileMarker = readUint16();
-      while (fileMarker !== 0xFFD9) {
+      markerLoop: while (fileMarker !== 0xFFD9) {
         var i, j, l;
         switch (fileMarker) {
           case 0xFFE0:
           case 0xFFE1:
           case 0xFFE2:
           case 0xFFE3:
           case 0xFFE4:
           case 0xFFE5:
@@ -28090,18 +28101,21 @@ var JpegImage = function JpegImageClosur
             var spectralStart = data[offset++];
             var spectralEnd = data[offset++];
             var successiveApproximation = data[offset++];
             try {
               var processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);
               offset += processed;
             } catch (ex) {
               if (ex instanceof DNLMarkerError) {
-                (0, _util.warn)('Attempting to re-parse JPEG image using "scanLines" ' + 'parameter found in DNL marker (0xFFDC) segment.');
+                (0, _util.warn)(`${ex.message} -- attempting to re-parse the JPEG image.`);
                 return this.parse(data, { dnlScanLines: ex.scanLines });
+              } else if (ex instanceof EOIMarkerError) {
+                (0, _util.warn)(`${ex.message} -- ignoring the rest of the image data.`);
+                break markerLoop;
               }
               throw ex;
             }
             break;
           case 0xFFDC:
             offset += 4;
             break;
           case 0xFFFF:
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -3202,23 +3202,19 @@ exports.OptionKind = OptionKind;
 
 /***/ }),
 /* 8 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
 let compatibilityParams = Object.create(null);
 ;
-const viewerCompatibilityParams = Object.freeze(compatibilityParams);
-exports.viewerCompatibilityParams = viewerCompatibilityParams;
+exports.viewerCompatibilityParams = Object.freeze(compatibilityParams);
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
deleted file mode 100644
--- a/dom/animation/test/css-animations/test_animation-finish.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<style>
-@keyframes anim {
-  from { margin-left: 100px; }
-  to { margin-left: 200px; }
-}
-</style>
-<body>
-<div id="log"></div>
-<script>
-
-'use strict';
-
-const ANIM_PROP_VAL = 'anim 100s';
-const ANIM_DURATION = 100000; // ms
-
-test(function(t) {
-  var div = addDiv(t);
-  div.style.animation = ANIM_PROP_VAL;
-  div.style.animationIterationCount = 'infinite';
-  var animation = div.getAnimations()[0];
-
-  var threw = false;
-  try {
-    animation.finish();
-  } catch (e) {
-    threw = true;
-    assert_equals(e.name, 'InvalidStateError',
-                  'Exception should be an InvalidStateError exception when ' +
-                  'trying to finish an infinite animation');
-  }
-  assert_true(threw,
-              'Expect InvalidStateError exception trying to finish an ' +
-              'infinite animation');
-}, 'Test exceptions when finishing infinite animation');
-
-async_test(function(t) {
-  var div = addDiv(t);
-  div.style.animation = ANIM_PROP_VAL + ' paused';
-  var animation = div.getAnimations()[0];
-
-  animation.ready.then(t.step_func(function() {
-    animation.finish();
-    assert_equals(animation.playState, 'finished',
-                  'The play state of a paused animation should become ' +
-                  '"finished" after finish() is called');
-    assert_times_equal(animation.startTime,
-                       animation.timeline.currentTime - ANIM_DURATION,
-                       'The start time of a paused animation should be set ' +
-                       'after calling finish()');
-    t.done();
-  }));
-}, 'Test finish() while paused');
-
-test(function(t) {
-  var div = addDiv(t);
-  div.style.animation = ANIM_PROP_VAL + ' paused';
-  var animation = div.getAnimations()[0];
-
-  // Update playbackRate so we can test that the calculated startTime
-  // respects it
-  animation.playbackRate = 2;
-
-  // While animation is still pause-pending call finish()
-  animation.finish();
-
-  assert_equals(animation.playState, 'finished',
-                'The play state of a pause-pending animation should become ' +
-                '"finished" after finish() is called');
-  assert_times_equal(animation.startTime,
-                     animation.timeline.currentTime - ANIM_DURATION / 2,
-                     'The start time of a pause-pending animation should ' +
-                     'be set after calling finish()');
-}, 'Test finish() while pause-pending with positive playbackRate');
-
-test(function(t) {
-  var div = addDiv(t);
-  div.style.animation = ANIM_PROP_VAL + ' paused';
-  var animation = div.getAnimations()[0];
-
-  animation.playbackRate = -2;
-  animation.finish();
-
-  assert_equals(animation.playState, 'finished',
-                'The play state of a pause-pending animation should become ' +
-                '"finished" after finish() is called');
-  assert_equals(animation.startTime, animation.timeline.currentTime,
-                'The start time of a pause-pending animation should be ' +
-                'set after calling finish()');
-}, 'Test finish() while pause-pending with negative playbackRate');
-
-</script>
-</body>
deleted file mode 100644
--- a/dom/animation/test/css-animations/test_animation-reverse.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<style>
-@keyframes anim {
-  to { transform: translate(100px) }
-}
-</style>
-<body>
-<div id="log"></div>
-<script>
-'use strict';
-
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-  div.style.animation = "";
-  flushComputedStyle(div);
-
-  assert_equals(animation.currentTime, null);
-  animation.reverse();
-
-  assert_equals(animation.currentTime, 100 * MS_PER_SEC,
-    'animation.currentTime should be its effect end');
-}, 'reverse() from idle state starts playing the animation');
-
-</script>
-</body>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -10,37 +10,16 @@ support-files =
   mozilla/file_disable_animations_api_core.html
   mozilla/file_discrete_animations.html
   mozilla/file_restyles.html
   mozilla/file_transition_finish_on_compositor.html
   ../../../layout/style/test/property_database.js
   testcommon.js
   !/dom/events/test/event_leak_utils.js
 
-[css-animations/test_animations-dynamic-changes.html]
-[css-animations/test_animation-cancel.html]
-[css-animations/test_animation-computed-timing.html]
-[css-animations/test_animation-currenttime.html]
-[css-animations/test_animation-finish.html]
-[css-animations/test_animation-finished.html]
-[css-animations/test_animation-id.html]
-[css-animations/test_animation-pausing.html]
-[css-animations/test_animation-playstate.html]
-[css-animations/test_animation-ready.html]
-[css-animations/test_animation-reverse.html]
-[css-animations/test_animation-starttime.html]
-[css-animations/test_cssanimation-animationname.html]
-[css-animations/test_document-get-animations.html]
-[css-animations/test_effect-target.html]
-[css-animations/test_element-get-animations.html]
-[css-animations/test_event-dispatch.html]
-[css-animations/test_event-order.html]
-[css-animations/test_keyframeeffect-getkeyframes.html]
-[css-animations/test_pseudoElement-get-animations.html]
-[css-animations/test_setting-effect.html]
 [css-transitions/test_animation-cancel.html]
 [css-transitions/test_animation-computed-timing.html]
 [css-transitions/test_animation-currenttime.html]
 [css-transitions/test_animation-finished.html]
 [css-transitions/test_animation-pausing.html]
 [css-transitions/test_animation-ready.html]
 [css-transitions/test_animation-starttime.html]
 [css-transitions/test_csstransition-transitionproperty.html]
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -769,29 +769,19 @@ WMFVideoMFTManager::InitInternal()
       mVideoInfo.ImageRect().x,
       mVideoInfo.ImageRect().y,
       mVideoInfo.ImageRect().width,
       mVideoInfo.ImageRect().height,
       mVideoInfo.mDisplay.width,
       mVideoInfo.mDisplay.height);
 
   if (!mUseHwAccel) {
-    RefPtr<ID3D11Device> device =
-      gfx::DeviceManagerDx::Get()->GetCompositorDevice();
-    if (!device) {
-      device = gfx::DeviceManagerDx::Get()->GetContentDevice();
-    }
+    RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
     if (device) {
-      RefPtr<ID3D10Multithread> multi;
-      HRESULT hr =
-        device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi));
-      if (SUCCEEDED(hr) && multi) {
-        multi->SetMultithreadProtected(TRUE);
-        mIMFUsable = true;
-      }
+      mIMFUsable = true;
     }
   }
   return MediaResult(NS_OK);
 }
 
 HRESULT
 WMFVideoMFTManager::SetDecoderMediaTypes()
 {
--- a/gfx/gl/EGLUtils.cpp
+++ b/gfx/gl/EGLUtils.cpp
@@ -1,84 +1,101 @@
 /* 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/. */
 
 #include "EGLUtils.h"
 
 #include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
 
 namespace mozilla {
 namespace gl {
 
 bool
 DoesEGLContextSupportSharingWithEGLImage(GLContext* gl)
 {
-    return sEGLLibrary.HasKHRImageBase() &&
-           sEGLLibrary.HasKHRImageTexture2D() &&
+    auto* egl = gl::GLLibraryEGL::Get();
+
+    return egl->HasKHRImageBase() &&
+           egl->HasKHRImageTexture2D() &&
            gl->IsExtensionSupported(GLContext::OES_EGL_image);
 }
 
 EGLImage
 CreateEGLImage(GLContext* gl, GLuint tex)
 {
     MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));
 
+    auto* egl = gl::GLLibraryEGL::Get();
+
     EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
     EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
-    EGLImage image = sEGLLibrary.fCreateImage(EGL_DISPLAY(),
-                                              eglContext,
-                                              LOCAL_EGL_GL_TEXTURE_2D,
-                                              clientBuffer,
-                                              nullptr);
+    EGLImage image = egl->fCreateImage(EGL_DISPLAY(),
+                                       eglContext,
+                                       LOCAL_EGL_GL_TEXTURE_2D,
+                                       clientBuffer,
+                                       nullptr);
     return image;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // EGLImageWrapper
 
 /*static*/ EGLImageWrapper*
 EGLImageWrapper::Create(GLContext* gl, GLuint tex)
 {
     MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));
 
-    GLLibraryEGL& library = sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
+
     EGLDisplay display = EGL_DISPLAY();
     EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
     EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
-    EGLImage image = library.fCreateImage(display,
-                                          eglContext,
-                                          LOCAL_EGL_GL_TEXTURE_2D,
-                                          clientBuffer,
-                                          nullptr);
+    EGLImage image = egl->fCreateImage(display,
+                                       eglContext,
+                                       LOCAL_EGL_GL_TEXTURE_2D,
+                                       clientBuffer,
+                                       nullptr);
     if (!image) {
 #ifdef DEBUG
         printf_stderr("Could not create EGL images: ERROR (0x%04x)\n",
-                      sEGLLibrary.fGetError());
+                      egl->fGetError());
 #endif
         return nullptr;
     }
 
-    return new EGLImageWrapper(library, display, image);
+    return new EGLImageWrapper(egl, display, image);
+}
+
+EGLImageWrapper::EGLImageWrapper(GLLibraryEGL* library,
+                                 EGLDisplay display,
+                                 EGLImage image)
+    : mLibrary(library)
+    , mDisplay(display)
+    , mImage(image)
+    , mSync(0)
+{
+    MOZ_ASSERT(mImage);
 }
 
 EGLImageWrapper::~EGLImageWrapper()
 {
-    mLibrary.fDestroyImage(mDisplay, mImage);
+    mLibrary->fDestroyImage(mDisplay, mImage);
 }
 
 bool
 EGLImageWrapper::FenceSync(GLContext* gl)
 {
     MOZ_ASSERT(!mSync);
 
-    if (mLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) {
-        mSync = mLibrary.fCreateSync(mDisplay,
-                                     LOCAL_EGL_SYNC_FENCE,
-                                     nullptr);
+    if (mLibrary->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) {
+        mSync = mLibrary->fCreateSync(mDisplay,
+                                      LOCAL_EGL_SYNC_FENCE,
+                                      nullptr);
         // We need to flush to make sure the sync object enters the command stream;
         // we can't use EGL_SYNC_FLUSH_COMMANDS_BIT at wait time, because the wait
         // happens on a different thread/context.
         gl->fFlush();
     }
 
     if (!mSync) {
         // we failed to create one, so just do a finish
@@ -95,20 +112,20 @@ EGLImageWrapper::ClientWaitSync()
         // if we have no sync object, then we did a Finish() earlier
         return true;
     }
 
     // wait at most 1 second; this should really be never/rarely hit
     const uint64_t ns_per_ms = 1000 * 1000;
     EGLTime timeout = 1000 * ns_per_ms;
 
-    EGLint result = mLibrary.fClientWaitSync(mDisplay,
-                                             mSync,
-                                             0,
-                                             timeout);
-    mLibrary.fDestroySync(mDisplay, mSync);
+    EGLint result = mLibrary->fClientWaitSync(mDisplay,
+                                              mSync,
+                                              0,
+                                              timeout);
+    mLibrary->fDestroySync(mDisplay, mSync);
     mSync = nullptr;
 
     return result == LOCAL_EGL_CONDITION_SATISFIED;
 }
 
 } // namespace gl
 } // namespace mozilla
--- a/gfx/gl/EGLUtils.h
+++ b/gfx/gl/EGLUtils.h
@@ -21,34 +21,26 @@ EGLImage CreateEGLImage(GLContext* gl, G
 // EGLImageWrapper
 
 class EGLImageWrapper
 {
 public:
     static EGLImageWrapper* Create(GLContext* gl, GLuint tex);
 
 private:
-    GLLibraryEGL& mLibrary;
+    const RefPtr<GLLibraryEGL> mLibrary;
     const EGLDisplay mDisplay;
 public:
     const EGLImage mImage;
 private:
     EGLSync mSync;
 
-    EGLImageWrapper(GLLibraryEGL& library,
+    EGLImageWrapper(GLLibraryEGL* library,
                     EGLDisplay display,
-                    EGLImage image)
-        : mLibrary(library)
-        , mDisplay(display)
-        , mImage(image)
-        , mSync(0)
-    {
-        MOZ_ASSERT(mImage);
-    }
-
+                    EGLImage image);
 public:
     ~EGLImageWrapper();
 
     // Insert a sync point on the given context, which should be the current active
     // context.
     bool FenceSync(GLContext* gl);
 
     bool ClientWaitSync();
--- a/gfx/gl/GLBlitHelperD3D.cpp
+++ b/gfx/gl/GLBlitHelperD3D.cpp
@@ -17,40 +17,40 @@
 #include "mozilla/layers/TextureD3D11.h"
 
 namespace mozilla {
 namespace gl {
 
 static EGLStreamKHR
 StreamFromD3DTexture(ID3D11Texture2D* const texD3D, const EGLAttrib* const postAttribs)
 {
-    auto& egl = sEGLLibrary;
-    if (!egl.IsExtensionSupported(GLLibraryEGL::NV_stream_consumer_gltexture_yuv) ||
-        !egl.IsExtensionSupported(GLLibraryEGL::ANGLE_stream_producer_d3d_texture))
+    auto* egl = gl::GLLibraryEGL::Get();
+    if (!egl->IsExtensionSupported(GLLibraryEGL::NV_stream_consumer_gltexture_yuv) ||
+        !egl->IsExtensionSupported(GLLibraryEGL::ANGLE_stream_producer_d3d_texture))
     {
         return 0;
     }
 
-    const auto& display = egl.Display();
-    const auto stream = egl.fCreateStreamKHR(display, nullptr);
+    const auto& display = egl->Display();
+    const auto stream = egl->fCreateStreamKHR(display, nullptr);
     MOZ_ASSERT(stream);
     if (!stream)
         return 0;
     bool ok = true;
-    MOZ_ALWAYS_TRUE( ok &= bool(egl.fStreamConsumerGLTextureExternalAttribsNV(display,
-                                                                              stream,
-                                                                              nullptr)) );
-    MOZ_ALWAYS_TRUE( ok &= bool(egl.fCreateStreamProducerD3DTextureANGLE(display, stream,
-                                                                         nullptr)) );
-    MOZ_ALWAYS_TRUE( ok &= bool(egl.fStreamPostD3DTextureANGLE(display, stream, texD3D,
-                                                               postAttribs)) );
+    MOZ_ALWAYS_TRUE( ok &= bool(egl->fStreamConsumerGLTextureExternalAttribsNV(display,
+                                                                               stream,
+                                                                               nullptr)) );
+    MOZ_ALWAYS_TRUE( ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(display, stream,
+                                                                          nullptr)) );
+    MOZ_ALWAYS_TRUE( ok &= bool(egl->fStreamPostD3DTextureANGLE(display, stream, texD3D,
+                                                                postAttribs)) );
     if (ok)
         return stream;
 
-    (void)egl.fDestroyStreamKHR(display, stream);
+    (void)egl->fDestroyStreamKHR(display, stream);
     return 0;
 }
 
 static RefPtr<ID3D11Texture2D>
 OpenSharedTexture(ID3D11Device* const d3d, const WindowsHandle handle)
 {
     RefPtr<ID3D11Texture2D> tex;
     auto hr = d3d->OpenSharedResource((HANDLE)handle, __uuidof(ID3D11Texture2D),
@@ -83,35 +83,35 @@ public:
         , mMultiTex(mParent.mGL, mNumPlanes, LOCAL_GL_TEXTURE_EXTERNAL)
         , mTempTexs{0}
         , mStreams{0}
         , mSuccess(true)
     {
         MOZ_RELEASE_ASSERT(numPlanes >= 1 && numPlanes <= 3);
 
         const auto& gl = mParent.mGL;
-        auto& egl = sEGLLibrary;
-        const auto& display = egl.Display();
+        auto* egl = gl::GLLibraryEGL::Get();
+        const auto& display = egl->Display();
 
         gl->fGenTextures(numPlanes, mTempTexs);
 
         for (uint8_t i = 0; i < mNumPlanes; i++) {
             gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
             gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTempTexs[i]);
             const EGLAttrib* postAttribs = nullptr;
             if (postAttribsList) {
                 postAttribs = postAttribsList[i];
             }
             mStreams[i] = StreamFromD3DTexture(texD3DList[i], postAttribs);
             mSuccess &= bool(mStreams[i]);
         }
 
         if (mSuccess) {
             for (uint8_t i = 0; i < mNumPlanes; i++) {
-                MOZ_ALWAYS_TRUE( egl.fStreamConsumerAcquireKHR(display, mStreams[i]) );
+                MOZ_ALWAYS_TRUE( egl->fStreamConsumerAcquireKHR(display, mStreams[i]) );
 
                 auto& mutex = mMutexList[i];
                 texD3DList[i]->QueryInterface(IID_IDXGIKeyedMutex,
                                               (void**)getter_AddRefs(mutex));
                 if (mutex) {
                     const auto hr = mutex->AcquireSync(0, 100);
                     if (FAILED(hr)) {
                         NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
@@ -120,30 +120,30 @@ public:
                 }
             }
         }
     }
 
     ~BindAnglePlanes()
     {
         const auto& gl = mParent.mGL;
-        auto& egl = sEGLLibrary;
-        const auto& display = egl.Display();
+        auto* egl = gl::GLLibraryEGL::Get();
+        const auto& display = egl->Display();
 
         if (mSuccess) {
             for (uint8_t i = 0; i < mNumPlanes; i++) {
-                MOZ_ALWAYS_TRUE( egl.fStreamConsumerReleaseKHR(display, mStreams[i]) );
+                MOZ_ALWAYS_TRUE( egl->fStreamConsumerReleaseKHR(display, mStreams[i]) );
                 if (mMutexList[i]) {
                     mMutexList[i]->ReleaseSync(0);
                 }
             }
         }
 
         for (uint8_t i = 0; i < mNumPlanes; i++) {
-            (void)egl.fDestroyStreamKHR(display, mStreams[i]);
+            (void)egl->fDestroyStreamKHR(display, mStreams[i]);
         }
 
         gl->fDeleteTextures(mNumPlanes, mTempTexs);
     }
 
     const bool& Success() const { return mSuccess; }
 };
 
@@ -153,22 +153,22 @@ ID3D11Device*
 GLBlitHelper::GetD3D11() const
 {
     if (mD3D11)
         return mD3D11;
 
     if (!mGL->IsANGLE())
         return nullptr;
 
-    auto& egl = sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     EGLDeviceEXT deviceEGL = 0;
-    MOZ_ALWAYS_TRUE( egl.fQueryDisplayAttribEXT(egl.Display(), LOCAL_EGL_DEVICE_EXT,
-                                                (EGLAttrib*)&deviceEGL) );
-    if (!egl.fQueryDeviceAttribEXT(deviceEGL, LOCAL_EGL_D3D11_DEVICE_ANGLE,
-                                   (EGLAttrib*)(ID3D11Device**)getter_AddRefs(mD3D11)))
+    MOZ_ALWAYS_TRUE( egl->fQueryDisplayAttribEXT(egl->Display(), LOCAL_EGL_DEVICE_EXT,
+                                                 (EGLAttrib*)&deviceEGL) );
+    if (!egl->fQueryDeviceAttribEXT(deviceEGL, LOCAL_EGL_D3D11_DEVICE_ANGLE,
+                                    (EGLAttrib*)(ID3D11Device**)getter_AddRefs(mD3D11)))
     {
         MOZ_ASSERT(false, "d3d9?");
         return nullptr;
     }
     return mD3D11;
 }
 
 // -------------------------------------
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -52,21 +52,21 @@ public:
         return mIsDoubleBuffered;
     }
 
     void SetIsDoubleBuffered(bool aIsDB) {
         mIsDoubleBuffered = aIsDB;
     }
 
     virtual bool IsANGLE() const override {
-        return sEGLLibrary.IsANGLE();
+        return GLLibraryEGL::Get()->IsANGLE();
     }
 
     virtual bool IsWARP() const override {
-        return sEGLLibrary.IsWARP();
+        return GLLibraryEGL::Get()->IsWARP();
     }
 
     virtual bool BindTexImage() override;
 
     virtual bool ReleaseTexImage() override;
 
     void SetEGLSurfaceOverride(EGLSurface surf);
     EGLSurface GetEGLSurfaceOverride() {
@@ -91,36 +91,39 @@ public:
     // for the lifetime of this context.
     void HoldSurface(gfxASurface* aSurf);
 
     EGLSurface GetEGLSurface() const {
         return mSurface;
     }
 
     EGLDisplay GetEGLDisplay() const {
-        return sEGLLibrary.Display();
+        return GLLibraryEGL::Get()->Display();
     }
 
     bool BindTex2DOffscreen(GLContext* aOffscreen);
     void UnbindTex2DOffscreen(GLContext* aOffscreen);
     void BindOffscreenFramebuffer();
 
+    void Destroy();
+
     static already_AddRefed<GLContextEGL>
     CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                      const gfx::IntSize& size,
                                      const SurfaceCaps& minCaps,
                                      nsACString* const out_FailureId);
 
 protected:
     friend class GLContextProviderEGL;
     friend class GLContextEGLFactory;
 
 public:
     const EGLConfig mConfig;
 protected:
+    const RefPtr<GLLibraryEGL> mEgl;
     EGLSurface mSurface;
     const EGLSurface mFallbackSurface;
 public:
     const EGLContext mContext;
 protected:
     EGLSurface mSurfaceOverride;
     RefPtr<gfxASurface> mThebesSurface;
     bool mBound;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -154,69 +154,74 @@ is_power_of_two(int v)
     if (v == 0)
         return true;
 
     return (v & (v-1)) == 0;
 }
 
 static void
 DestroySurface(EGLSurface oldSurface) {
+    auto* egl = gl::GLLibraryEGL::Get();
+
     if (oldSurface != EGL_NO_SURFACE) {
         // TODO: This breaks TLS MakeCurrent caching.
-        sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
-                                 EGL_NO_SURFACE, EGL_NO_SURFACE,
-                                 EGL_NO_CONTEXT);
-        sEGLLibrary.fDestroySurface(EGL_DISPLAY(), oldSurface);
+        egl->fMakeCurrent(EGL_DISPLAY(),
+                          EGL_NO_SURFACE, EGL_NO_SURFACE,
+                          EGL_NO_CONTEXT);
+        egl->fDestroySurface(EGL_DISPLAY(), oldSurface);
 #if defined(MOZ_WAYLAND)
         DeleteWaylandGLSurface(oldSurface);
 #endif
     }
 }
 
 static EGLSurface
 CreateFallbackSurface(const EGLConfig& config)
 {
-    if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_surfaceless_context)) {
+    auto* egl = gl::GLLibraryEGL::Get();
+
+    if (egl->IsExtensionSupported(GLLibraryEGL::KHR_surfaceless_context)) {
         // We don't need a PBuffer surface in this case
         return EGL_NO_SURFACE;
     }
 
     std::vector<EGLint> pbattrs;
     pbattrs.push_back(LOCAL_EGL_WIDTH); pbattrs.push_back(1);
     pbattrs.push_back(LOCAL_EGL_HEIGHT); pbattrs.push_back(1);
 
     for (const auto& cur : kTerminationAttribs) {
         pbattrs.push_back(cur);
     }
 
-    EGLSurface surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, pbattrs.data());
+    EGLSurface surface = egl->fCreatePbufferSurface(EGL_DISPLAY(), config, pbattrs.data());
     if (!surface) {
         MOZ_CRASH("Failed to create fallback EGLSurface");
     }
 
     return surface;
 }
 
 static EGLSurface
 CreateSurfaceFromNativeWindow(EGLNativeWindowType window, const EGLConfig& config)
 {
     MOZ_ASSERT(window);
+    auto* egl = gl::GLLibraryEGL::Get();
     EGLSurface newSurface = EGL_NO_SURFACE;
 
 #ifdef MOZ_WIDGET_ANDROID
     JNIEnv* const env = jni::GetEnvForThread();
     ANativeWindow* const nativeWindow = ANativeWindow_fromSurface(
             env, reinterpret_cast<jobject>(window));
-    newSurface = sEGLLibrary.fCreateWindowSurface(
-            sEGLLibrary.fGetDisplay(EGL_DEFAULT_DISPLAY),
+    newSurface = egl->fCreateWindowSurface(
+            egl->fGetDisplay(EGL_DEFAULT_DISPLAY),
             config, nativeWindow, 0);
     ANativeWindow_release(nativeWindow);
 #else
-    newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config,
-                                                  window, 0);
+    newSurface = egl->fCreateWindowSurface(EGL_DISPLAY(), config,
+                                           window, 0);
 #endif
     return newSurface;
 }
 
 /* GLContextEGLFactory class was added as a friend of GLContextEGL
  * so that it could access  GLContextEGL::CreateGLContext. This was
  * done so that a new function would not need to be added to the shared
  * GLContextProvider interface.
@@ -230,25 +235,26 @@ private:
     ~GLContextEGLFactory(){}
 };
 
 already_AddRefed<GLContext>
 GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
                             bool aWebRender)
 {
     nsCString discardFailureId;
-    if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+    if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
         gfxCriticalNote << "Failed to load EGL library 3!";
         return nullptr;
     }
 
+    auto* egl = gl::GLLibraryEGL::Get();
     bool doubleBuffered = true;
 
     EGLConfig config;
-    if (aWebRender && sEGLLibrary.IsANGLE()) {
+    if (aWebRender && egl->IsANGLE()) {
         // Force enable alpha channel to make sure ANGLE use correct framebuffer formart
         const int bpp = 32;
         const bool withDepth = true;
         if (!CreateConfig(&config, bpp, withDepth)) {
             gfxCriticalNote << "Failed to create EGLConfig for WebRender ANGLE!";
             return nullptr;
         }
     } else {
@@ -273,28 +279,29 @@ GLContextEGLFactory::Create(EGLNativeWin
     if (!gl) {
         gfxCriticalNote << "Failed to create EGLContext!";
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
     gl->MakeCurrent();
     gl->SetIsDoubleBuffered(doubleBuffered);
-    if (aWebRender && sEGLLibrary.IsANGLE()) {
+    if (aWebRender && egl->IsANGLE()) {
         MOZ_ASSERT(doubleBuffered);
-        sEGLLibrary.fSwapInterval(EGL_DISPLAY(), 0);
+        egl->fSwapInterval(EGL_DISPLAY(), 0);
     }
     return gl.forget();
 }
 
 GLContextEGL::GLContextEGL(CreateContextFlags flags, const SurfaceCaps& caps,
                            bool isOffscreen, EGLConfig config, EGLSurface surface,
                            EGLContext context)
     : GLContext(flags, caps, nullptr, isOffscreen, false)
     , mConfig(config)
+    , mEgl(gl::GLLibraryEGL::Get())
     , mSurface(surface)
     , mFallbackSurface(CreateFallbackSurface(config))
     , mContext(context)
     , mSurfaceOverride(EGL_NO_SURFACE)
     , mThebesSurface(nullptr)
     , mBound(false)
     , mIsPBuffer(false)
     , mIsDoubleBuffered(false)
@@ -315,17 +322,17 @@ GLContextEGL::~GLContextEGL()
     if (!mOwnsContext) {
         return;
     }
 
 #ifdef DEBUG
     printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
 #endif
 
-    sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext);
+    mEgl->fDestroyContext(EGL_DISPLAY(), mContext);
 
     mozilla::gl::DestroySurface(mSurface);
     mozilla::gl::DestroySurface(mFallbackSurface);
 }
 
 bool
 GLContextEGL::Init()
 {
@@ -352,33 +359,33 @@ GLContextEGL::Init()
         gfx::LogFailure(NS_LITERAL_CSTRING(
             "Couldn't get device attachments for device."));
         return false;
     }
 
     static_assert(sizeof(GLint) >= sizeof(int32_t), "GLint is smaller than int32_t");
     mMaxTextureImageSize = INT32_MAX;
 
-    mShareWithEGLImage = sEGLLibrary.HasKHRImageBase() &&
-                          sEGLLibrary.HasKHRImageTexture2D() &&
-                          IsExtensionSupported(OES_EGL_image);
+    mShareWithEGLImage = mEgl->HasKHRImageBase() &&
+                         mEgl->HasKHRImageTexture2D() &&
+                         IsExtensionSupported(OES_EGL_image);
 
     return true;
 }
 
 bool
 GLContextEGL::BindTexImage()
 {
     if (!mSurface)
         return false;
 
     if (mBound && !ReleaseTexImage())
         return false;
 
-    EGLBoolean success = sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
+    EGLBoolean success = mEgl->fBindTexImage(EGL_DISPLAY(),
         (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
     if (success == LOCAL_EGL_FALSE)
         return false;
 
     mBound = true;
     return true;
 }
 
@@ -387,19 +394,19 @@ GLContextEGL::ReleaseTexImage()
 {
     if (!mBound)
         return true;
 
     if (!mSurface)
         return false;
 
     EGLBoolean success;
-    success = sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
-                                            (EGLSurface)mSurface,
-                                            LOCAL_EGL_BACK_BUFFER);
+    success = mEgl->fReleaseTexImage(EGL_DISPLAY(),
+                                     (EGLSurface)mSurface,
+                                     LOCAL_EGL_BACK_BUFFER);
     if (success == LOCAL_EGL_FALSE)
         return false;
 
     mBound = false;
     return true;
 }
 
 void
@@ -422,20 +429,20 @@ bool
 GLContextEGL::MakeCurrentImpl() const
 {
     EGLSurface surface = (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride
                                                               : mSurface;
     if (!surface) {
         surface = mFallbackSurface;
     }
 
-    const bool succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), surface, surface,
-                                                    mContext);
+    const bool succeeded = mEgl->fMakeCurrent(EGL_DISPLAY(), surface, surface,
+                                              mContext);
     if (!succeeded) {
-        const auto eglError = sEGLLibrary.fGetError();
+        const auto eglError = mEgl->fGetError();
         if (eglError == LOCAL_EGL_CONTEXT_LOST) {
             mContextLost = true;
             NS_WARNING("EGL context has been lost.");
         } else {
             NS_WARNING("Failed to make GL context current!");
 #ifdef DEBUG
             printf_stderr("EGL Error: 0x%04x\n", eglError);
 #endif
@@ -443,17 +450,17 @@ GLContextEGL::MakeCurrentImpl() const
     }
 
     return succeeded;
 }
 
 bool
 GLContextEGL::IsCurrentImpl() const
 {
-    return sEGLLibrary.fGetCurrentContext() == mContext;
+    return mEgl->fGetCurrentContext() == mContext;
 }
 
 bool
 GLContextEGL::RenewSurface(CompositorWidget* aWidget) {
     if (!mOwnsContext) {
         return false;
     }
     // unconditionally release the surface and create a new one. Don't try to optimize this away.
@@ -482,48 +489,48 @@ GLContextEGL::ReleaseSurface() {
         mSurfaceOverride = EGL_NO_SURFACE;
     }
     mSurface = EGL_NO_SURFACE;
 }
 
 bool
 GLContextEGL::SetupLookupFunction()
 {
-    mLookupFunc = sEGLLibrary.GetLookupFunction();
+    mLookupFunc = mEgl->GetLookupFunction();
     return true;
 }
 
 bool
 GLContextEGL::SwapBuffers()
 {
     EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
                           ? mSurfaceOverride
                           : mSurface;
     if (surface) {
-        return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), surface);
+        return mEgl->fSwapBuffers(EGL_DISPLAY(), surface);
     } else {
         return false;
     }
 }
 
 void
 GLContextEGL::GetWSIInfo(nsCString* const out) const
 {
     out->AppendLiteral("EGL_VENDOR: ");
-    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VENDOR));
+    out->Append((const char*)mEgl->fQueryString(EGL_DISPLAY(), LOCAL_EGL_VENDOR));
 
     out->AppendLiteral("\nEGL_VERSION: ");
-    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VERSION));
+    out->Append((const char*)mEgl->fQueryString(EGL_DISPLAY(), LOCAL_EGL_VERSION));
 
     out->AppendLiteral("\nEGL_EXTENSIONS: ");
-    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_EXTENSIONS));
+    out->Append((const char*)mEgl->fQueryString(EGL_DISPLAY(), LOCAL_EGL_EXTENSIONS));
 
 #ifndef ANDROID // This query will crash some old android.
     out->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
-    out->Append((const char*)sEGLLibrary.fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
+    out->Append((const char*)mEgl->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
 #endif
 }
 
 // hold a reference to the given surface
 // for the lifetime of this context.
 void
 GLContextEGL::HoldSurface(gfxASurface* aSurf) {
     mThebesSurface = aSurf;
@@ -532,60 +539,62 @@ GLContextEGL::HoldSurface(gfxASurface* a
 already_AddRefed<GLContextEGL>
 GLContextEGL::CreateGLContext(CreateContextFlags flags,
                 const SurfaceCaps& caps,
                 bool isOffscreen,
                 EGLConfig config,
                 EGLSurface surface,
                 nsACString* const out_failureId)
 {
-    if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
+    auto* egl = gl::GLLibraryEGL::Get();
+
+    if (egl->fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
         NS_WARNING("Failed to bind API to GLES!");
         return nullptr;
     }
 
     std::vector<EGLint> required_attribs;
     required_attribs.push_back(LOCAL_EGL_CONTEXT_CLIENT_VERSION);
     if (flags & CreateContextFlags::PREFER_ES3) {
         required_attribs.push_back(3);
     } else {
         required_attribs.push_back(2);
     }
 
     std::vector<EGLint> robustness_attribs;
     std::vector<EGLint> rbab_attribs; // RBAB: Robust Buffer Access Behavior
     if (flags & CreateContextFlags::PREFER_ROBUSTNESS) {
-        if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EXT_create_context_robustness)) {
+        if (egl->IsExtensionSupported(GLLibraryEGL::EXT_create_context_robustness)) {
             robustness_attribs = required_attribs;
             robustness_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
             robustness_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT);
             // Skip EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, since it doesn't help us.
         }
 
-        if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_create_context) &&
-            !sEGLLibrary.IsANGLE())
+        if (egl->IsExtensionSupported(GLLibraryEGL::KHR_create_context) &&
+            !egl->IsANGLE())
         {
             rbab_attribs = required_attribs;
             rbab_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
             rbab_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR);
             rbab_attribs.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR);
             rbab_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR);
         }
     }
 
     const auto fnCreate = [&](const std::vector<EGLint>& attribs) {
         auto terminated_attribs = attribs;
 
         for (const auto& cur : kTerminationAttribs) {
             terminated_attribs.push_back(cur);
         }
 
-        return sEGLLibrary.fCreateContext(EGL_DISPLAY(), config, EGL_NO_CONTEXT,
-                                          terminated_attribs.data());
+        return egl->fCreateContext(EGL_DISPLAY(), config, EGL_NO_CONTEXT,
+                                   terminated_attribs.data());
     };
 
     EGLContext context;
     do {
         if (!rbab_attribs.empty()) {
             context = fnCreate(rbab_attribs);
             if (context)
                 break;
@@ -639,17 +648,19 @@ TRY_AGAIN_POWER_OF_TWO:
         pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
         pbattrs.AppendElement(bindToTextureFormat);
     }
 
     for (const auto& cur : kTerminationAttribs) {
         pbattrs.AppendElement(cur);
     }
 
-    surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
+    auto* egl = gl::GLLibraryEGL::Get();
+
+    surface = egl->fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
     if (!surface) {
         if (!is_power_of_two(pbsize.width) ||
             !is_power_of_two(pbsize.height))
         {
             if (!is_power_of_two(pbsize.width))
                 pbsize.width = next_power_of_two(pbsize.width);
             if (!is_power_of_two(pbsize.height))
                 pbsize.height = next_power_of_two(pbsize.height);
@@ -769,40 +780,42 @@ CreateConfig(EGLConfig* aConfig, int32_t
         case 32:
             attribs = kEGLConfigAttribsRGBA32;
             break;
         default:
             NS_ERROR("Unknown pixel depth");
             return false;
     }
 
-    if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs,
-                                   configs, ncfg, &ncfg) ||
+    auto* egl = gl::GLLibraryEGL::Get();
+
+    if (!egl->fChooseConfig(EGL_DISPLAY(), attribs,
+                            configs, ncfg, &ncfg) ||
         ncfg < 1) {
         return false;
     }
 
     for (int j = 0; j < ncfg; ++j) {
         EGLConfig config = configs[j];
         EGLint r, g, b, a;
-        if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                         LOCAL_EGL_RED_SIZE, &r) &&
-            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                         LOCAL_EGL_GREEN_SIZE, &g) &&
-            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                         LOCAL_EGL_BLUE_SIZE, &b) &&
-            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                         LOCAL_EGL_ALPHA_SIZE, &a) &&
+        if (egl->fGetConfigAttrib(EGL_DISPLAY(), config,
+                                  LOCAL_EGL_RED_SIZE, &r) &&
+            egl->fGetConfigAttrib(EGL_DISPLAY(), config,
+                                  LOCAL_EGL_GREEN_SIZE, &g) &&
+            egl->fGetConfigAttrib(EGL_DISPLAY(), config,
+                                  LOCAL_EGL_BLUE_SIZE, &b) &&
+            egl->fGetConfigAttrib(EGL_DISPLAY(), config,
+                                  LOCAL_EGL_ALPHA_SIZE, &a) &&
             ((depth == 16 && r == 5 && g == 6 && b == 5) ||
              (depth == 24 && r == 8 && g == 8 && b == 8) ||
              (depth == 32 && r == 8 && g == 8 && b == 8 && a == 8)))
         {
             EGLint z;
             if (aEnableDepthBuffer) {
-                if (!sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, LOCAL_EGL_DEPTH_SIZE, &z) ||
+                if (!egl->fGetConfigAttrib(EGL_DISPLAY(), config, LOCAL_EGL_DEPTH_SIZE, &z) ||
                     z != 24) {
                     continue;
                 }
             }
             *aConfig = config;
             return true;
         }
     }
@@ -836,17 +849,17 @@ CreateConfig(EGLConfig* aConfig, bool aE
         return true;
     }
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
 {
     nsCString discardFailureId;
-    if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+    if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 2!");
         return nullptr;
     }
 
     if (!aContext || !aSurface)
         return nullptr;
 
     SurfaceCaps caps = SurfaceCaps::Any();
@@ -889,44 +902,45 @@ GLContextEGL::CreateCompatibleSurface(vo
     return GLContextProviderEGL::CreateEGLSurface(aWindow, mConfig);
 }
 
 /* static */ EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow, EGLConfig aConfig)
 {
     // NOTE: aWindow is an ANativeWindow
     nsCString discardFailureId;
-    if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+    if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 4!");
     }
+    auto* egl = gl::GLLibraryEGL::Get();
     EGLConfig config = aConfig;
     if (!config && !CreateConfig(&config, /* aEnableDepthBuffer */ false)) {
         MOZ_CRASH("GFX: Failed to create EGLConfig 2!");
     }
 
     MOZ_ASSERT(aWindow);
 
-    EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow,
-                                                          0);
+    EGLSurface surface = egl->fCreateWindowSurface(EGL_DISPLAY(), config, aWindow,
+                                                   0);
     if (surface == EGL_NO_SURFACE) {
         MOZ_CRASH("GFX: Failed to create EGLSurface 2!");
     }
 
     return surface;
 }
 
 /* static */ void
 GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
 {
     nsCString discardFailureId;
-    if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+    if (!GLLibraryEGL::EnsureInitialized(false, &discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 5!");
     }
-
-    sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
+    auto* egl = gl::GLLibraryEGL::Get();
+    egl->fDestroySurface(EGL_DISPLAY(), surface);
 }
 #endif // defined(ANDROID)
 
 static void
 FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
                    bool es3, nsTArray<EGLint>* out)
 {
     out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
@@ -1037,30 +1051,31 @@ ChooseConfig(GLLibraryEGL* egl, CreateCo
 
 /*static*/ already_AddRefed<GLContextEGL>
 GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                                const mozilla::gfx::IntSize& size,
                                                const SurfaceCaps& minCaps,
                                                nsACString* const out_failureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) {
+    if (!GLLibraryEGL::EnsureInitialized(forceEnableHardware, out_failureId)) {
         return nullptr;
     }
 
+    auto* egl = gl::GLLibraryEGL::Get();
     SurfaceCaps configCaps;
-    EGLConfig config = ChooseConfig(&sEGLLibrary, flags, minCaps, &configCaps);
+    EGLConfig config = ChooseConfig(egl, flags, minCaps, &configCaps);
     if (config == EGL_NO_CONFIG) {
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_NO_CONFIG");
         NS_WARNING("Failed to find a compatible config.");
         return nullptr;
     }
 
     if (GLContext::ShouldSpew()) {
-        sEGLLibrary.DumpEGLConfig(config);
+        egl->DumpEGLConfig(config);
     }
 
     mozilla::gfx::IntSize pbSize(size);
     EGLSurface surface = nullptr;
 #if defined(MOZ_WAYLAND)
     if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
         surface = GLContextEGL::CreateWaylandBufferSurface(config, pbSize);
     } else
@@ -1076,17 +1091,17 @@ GLContextEGL::CreateEGLPBufferOffscreenC
         return nullptr;
     }
 
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, true,
                                                             config, surface,
                                                             out_failureId);
     if (!gl) {
         NS_WARNING("Failed to create GLContext from PBuffer");
-        sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
+        egl->fDestroySurface(egl->Display(), surface);
 #if defined(MOZ_WAYLAND)
         DeleteWaylandGLSurface(surface);
 #endif
         return nullptr;
     }
 
     return gl.forget();
 }
@@ -1105,22 +1120,23 @@ GLContextProviderEGL::CreateHeadless(Cre
 // often without the ability to texture from them directly.
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
                                       const SurfaceCaps& minCaps,
                                       CreateContextFlags flags,
                                       nsACString* const out_failureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) { // Needed for IsANGLE().
+    if (!GLLibraryEGL::EnsureInitialized(forceEnableHardware, out_failureId)) { // Needed for IsANGLE().
         return nullptr;
     }
 
+    auto* egl = gl::GLLibraryEGL::Get();
     bool canOffscreenUseHeadless = true;
-    if (sEGLLibrary.IsANGLE()) {
+    if (egl->IsANGLE()) {
         // ANGLE needs to use PBuffers.
         canOffscreenUseHeadless = false;
     }
 
 #if defined(MOZ_WIDGET_ANDROID)
     // Using a headless context loses the SurfaceCaps
     // which can cause a loss of depth and/or stencil
     canOffscreenUseHeadless = false;
@@ -1175,14 +1191,18 @@ GLContextProviderEGL::CreateOffscreen(co
 GLContextProviderEGL::GetGlobalContext()
 {
     return nullptr;
 }
 
 /*static*/ void
 GLContextProviderEGL::Shutdown()
 {
+    const RefPtr<GLLibraryEGL> egl = GLLibraryEGL::Get();
+    if (egl) {
+        egl->Shutdown();
+    }
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
 
 #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -38,17 +38,17 @@
 #include <dlfcn.h>
 #endif // MOZ_WIDGET_GTK
 #endif // MOZ_WAYLAND
 
 namespace mozilla {
 namespace gl {
 
 StaticMutex GLLibraryEGL::sMutex;
-GLLibraryEGL sEGLLibrary;
+StaticRefPtr<GLLibraryEGL> GLLibraryEGL::sEGLLibrary;
 
 // should match the order of EGLExtensions, and be null-terminated.
 static const char* sEGLExtensionNames[] = {
     "EGL_KHR_image_base",
     "EGL_KHR_image_pixmap",
     "EGL_KHR_gl_texture_2D_image",
     "EGL_KHR_lock_surface",
     "EGL_ANGLE_surface_d3d_texture_2d_share_handle",
@@ -360,19 +360,33 @@ GLLibraryEGL::ReadbackEGLImage(EGLImage 
                                                              out_surface->GetFormat());
     int shaderConfig = config.mFeatures;
     mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
                                                     out_surface->GetSize(), shaderConfig);
 
     return true;
 }
 
+/* static */ bool
+GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId) {
+    if (!sEGLLibrary) {
+        sEGLLibrary = new GLLibraryEGL();
+    }
+    return sEGLLibrary->DoEnsureInitialized(forceAccel, out_failureId);
+}
+
 bool
-GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId)
+GLLibraryEGL::DoEnsureInitialized(bool forceAccel, nsACString* const out_failureId)
 {
+    if (mInitialized && !mSymbols.fTerminate) {
+        *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_DESTROYED");
+        MOZ_ASSERT(false);
+        return false;
+    }
+
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
 #ifdef XP_WIN
     if (!mEGLLibrary) {
@@ -667,16 +681,30 @@ GLLibraryEGL::EnsureInitialized(bool for
     mInitialized = true;
     reporter.SetSuccessful();
     return true;
 }
 
 #undef SYMBOL
 #undef END_OF_SYMBOLS
 
+void
+GLLibraryEGL::Shutdown()
+{
+    if (this != sEGLLibrary) {
+        return;
+    }
+    if (mEGLDisplay) {
+        fTerminate(mEGLDisplay);
+        mEGLDisplay = EGL_NO_DISPLAY;
+    }
+    mSymbols = {};
+    sEGLLibrary = nullptr;
+}
+
 EGLDisplay
 GLLibraryEGL::CreateDisplay(bool forceAccel, const nsCOMPtr<nsIGfxInfo>& gfxInfo, nsACString* const out_failureId)
 {
     MOZ_ASSERT(!mInitialized);
 
     EGLDisplay chosenDisplay = nullptr;
 
     if (IsExtensionSupported(ANGLE_platform_angle_d3d)) {
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -52,17 +52,22 @@ namespace gl {
 
 class GLContext;
 
 void BeforeEGLCall(const char* funcName);
 void AfterEGLCall(const char* funcName);
 
 class GLLibraryEGL
 {
+protected:
+  ~GLLibraryEGL() {}
+
 public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLLibraryEGL)
+
     GLLibraryEGL()
         : mSymbols{nullptr}
         , mInitialized(false)
         , mEGLLibrary(nullptr)
         , mEGLDisplay(EGL_NO_DISPLAY)
         , mIsANGLE(false)
         , mIsWARP(false)
     { }
@@ -361,17 +366,23 @@ public:
     }
 
     bool HasANGLESurfaceD3DTexture2DShareHandle() {
         return IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle);
     }
 
     bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
 
-    bool EnsureInitialized(bool forceAccel, nsACString* const out_failureId);
+    inline static GLLibraryEGL* Get() {
+        return sEGLLibrary;
+    }
+
+    static bool EnsureInitialized(bool forceAccel, nsACString* const out_failureId);
+
+    void Shutdown();
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
 private:
     struct {
         EGLCastToRelevantPtr (GLAPIENTRY * fGetProcAddress)(const char* procname);
         EGLDisplay (GLAPIENTRY * fGetDisplay)(void* display_id);
@@ -484,30 +495,31 @@ private:
         EGLDeviceEXT (GLAPIENTRY * fCreateDeviceANGLE) (EGLint device_type,
                                                         void* native_device,
                                                         const EGLAttrib* attrib_list);
         EGLBoolean (GLAPIENTRY * fReleaseDeviceANGLE) (EGLDeviceEXT device);
 
     } mSymbols;
 
 private:
+    bool DoEnsureInitialized(bool forceAccel, nsACString* const out_failureId);
     EGLDisplay CreateDisplay(bool forceAccel,
                              const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                              nsACString* const out_failureId);
 
     bool mInitialized;
     PRLibrary* mEGLLibrary;
     EGLDisplay mEGLDisplay;
     RefPtr<GLContext> mReadbackGL;
 
     bool mIsANGLE;
     bool mIsWARP;
     static StaticMutex sMutex;
+    static StaticRefPtr<GLLibraryEGL> sEGLLibrary;
 };
 
-extern GLLibraryEGL sEGLLibrary;
-#define EGL_DISPLAY()        sEGLLibrary.Display()
+#define EGL_DISPLAY()        GLLibraryEGL::Get()->Display()
 
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* GLLIBRARYEGL_H_ */
 
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -41,17 +41,17 @@ CreatePBufferSurface(GLLibraryEGL* egl,
 
     return surface;
 }
 
 /*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
 SharedSurface_ANGLEShareHandle::Create(GLContext* gl, EGLConfig config,
                                        const gfx::IntSize& size, bool hasAlpha)
 {
-    GLLibraryEGL* egl = &sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     MOZ_ASSERT(egl);
     MOZ_ASSERT(egl->IsExtensionSupported(
                GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
     MOZ_ASSERT(config);
 
     EGLDisplay display = egl->Display();
     EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
     if (!pbuffer)
@@ -320,17 +320,17 @@ SharedSurface_ANGLEShareHandle::Readback
 ////////////////////////////////////////////////////////////////////////////////
 // Factory
 
 /*static*/ UniquePtr<SurfaceFactory_ANGLEShareHandle>
 SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl, const SurfaceCaps& caps,
                                         const RefPtr<layers::LayersIPCChannel>& allocator,
                                         const layers::TextureFlags& flags)
 {
-    GLLibraryEGL* egl = &sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     if (!egl)
         return nullptr;
 
     auto ext = GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle;
     if (!egl->IsExtensionSupported(ext))
         return nullptr;
 
     EGLConfig config = GLContextEGL::Cast(gl)->mConfig;
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -18,17 +18,17 @@ namespace gl {
 
 /*static*/ UniquePtr<SharedSurface_EGLImage>
 SharedSurface_EGLImage::Create(GLContext* prodGL,
                                const GLFormats& formats,
                                const gfx::IntSize& size,
                                bool hasAlpha,
                                EGLContext context)
 {
-    GLLibraryEGL* egl = &sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     MOZ_ASSERT(egl);
     MOZ_ASSERT(context);
 
     UniquePtr<SharedSurface_EGLImage> ret;
 
     if (!HasExtensions(egl, prodGL)) {
         return ret;
     }
@@ -152,32 +152,33 @@ SharedSurface_EGLImage::ToSurfaceDescrip
     return true;
 }
 
 bool
 SharedSurface_EGLImage::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
 {
     MOZ_ASSERT(out_surface);
     MOZ_ASSERT(NS_IsMainThread());
-    return sEGLLibrary.ReadbackEGLImage(mImage, out_surface);
+    auto* egl = gl::GLLibraryEGL::Get();
+    return egl->ReadbackEGLImage(mImage, out_surface);
 }
 
 ////////////////////////////////////////////////////////////////////////
 
 /*static*/ UniquePtr<SurfaceFactory_EGLImage>
 SurfaceFactory_EGLImage::Create(GLContext* prodGL, const SurfaceCaps& caps,
                                 const RefPtr<layers::LayersIPCChannel>& allocator,
                                 const layers::TextureFlags& flags)
 {
     EGLContext context = GLContextEGL::Cast(prodGL)->mContext;
 
     typedef SurfaceFactory_EGLImage ptrT;
     UniquePtr<ptrT> ret;
 
-    GLLibraryEGL* egl = &sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     if (SharedSurface_EGLImage::HasExtensions(egl, prodGL)) {
         ret.reset( new ptrT(prodGL, caps, allocator, flags, context) );
     }
 
     return ret;
 }
 
 ////////////////////////////////////////////////////////////////////////
--- a/gfx/gl/TextureImageEGL.cpp
+++ b/gfx/gl/TextureImageEGL.cpp
@@ -167,53 +167,56 @@ TextureImageEGL::Resize(const gfx::IntSi
 }
 
 bool
 TextureImageEGL::BindTexImage()
 {
     if (mBound && !ReleaseTexImage())
         return false;
 
+    auto* egl = gl::GLLibraryEGL::Get();
     EGLBoolean success =
-        sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
-                                  (EGLSurface)mSurface,
-                                  LOCAL_EGL_BACK_BUFFER);
+        egl->fBindTexImage(EGL_DISPLAY(),
+                           (EGLSurface)mSurface,
+                           LOCAL_EGL_BACK_BUFFER);
 
     if (success == LOCAL_EGL_FALSE)
         return false;
 
     mBound = true;
     return true;
 }
 
 bool
 TextureImageEGL::ReleaseTexImage()
 {
     if (!mBound)
         return true;
 
+    auto* egl = gl::GLLibraryEGL::Get();
     EGLBoolean success =
-        sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
-                                      (EGLSurface)mSurface,
-                                      LOCAL_EGL_BACK_BUFFER);
+        egl->fReleaseTexImage(EGL_DISPLAY(),
+                              (EGLSurface)mSurface,
+                              LOCAL_EGL_BACK_BUFFER);
 
     if (success == LOCAL_EGL_FALSE)
         return false;
 
     mBound = false;
     return true;
 }
 
 void
 TextureImageEGL::DestroyEGLSurface(void)
 {
     if (!mSurface)
         return;
 
-    sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface);
+    auto* egl = gl::GLLibraryEGL::Get();
+    egl->fDestroySurface(EGL_DISPLAY(), mSurface);
     mSurface = nullptr;
 }
 
 already_AddRefed<TextureImage>
 CreateTextureImageEGL(GLContext* gl,
                       const gfx::IntSize& aSize,
                       TextureImage::ContentType aContentType,
                       GLenum aWrapMode,
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -6,16 +6,17 @@
 #ifdef XP_WIN
 #include "WMF.h"
 #endif
 #include "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
+#include "GLContextProvider.h"
 #include "GPUProcessHost.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
 #include "mozilla/gfx/2D.h"
@@ -493,16 +494,30 @@ GPUParent::ActorDestroy(ActorDestroyReas
   dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
   VRListenerThreadHolder::Shutdown();
   // There is a case that RenderThread exists when gfxVars::UseWebRender() is false.
   // This could happen when WebRender was fallbacked to compositor.
   if (wr::RenderThread::Get()) {
     wr::RenderThread::ShutDown();
   }
+
+  // Shut down the default GL context provider.
+  gl::GLContextProvider::Shutdown();
+
+#if defined(XP_WIN)
+  // The above shutdown calls operate on the available context providers on
+  // most platforms.  Windows is a "special snowflake", though, and has three
+  // context providers available, so we have to shut all of them down.
+  // We should only support the default GL provider on Windows; then, this
+  // could go away. Unfortunately, we currently support WGL (the default) for
+  // WebGL on Optimus.
+  gl::GLContextProviderEGL::Shutdown();
+#endif
+
   Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
 #endif
   LayerTreeOwnerTracker::Shutdown();
   gfxVars::Shutdown();
   gfxConfig::Shutdown();
   gfxPrefs::DestroySingleton();
--- a/gfx/layers/opengl/EGLImageHelpers.cpp
+++ b/gfx/layers/opengl/EGLImageHelpers.cpp
@@ -26,28 +26,30 @@ EGLImageCreateFromNativeBuffer(GLContext
         LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
         LOCAL_EGL_IMAGE_CROP_LEFT_ANDROID, 0,
         LOCAL_EGL_IMAGE_CROP_TOP_ANDROID, 0,
         LOCAL_EGL_IMAGE_CROP_RIGHT_ANDROID, aCropSize.width,
         LOCAL_EGL_IMAGE_CROP_BOTTOM_ANDROID, aCropSize.height,
         LOCAL_EGL_NONE, LOCAL_EGL_NONE,
     };
 
+    auto* egl = gl::GLLibraryEGL::Get();
     bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0);
     EGLint* usedAttrs = attrs;
-    if (hasCropRect && sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) {
+    if (hasCropRect && egl->IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) {
         usedAttrs = cropAttrs;
     }
 
-    return sEGLLibrary.fCreateImage(sEGLLibrary.Display(),
-                                     EGL_NO_CONTEXT,
-                                     LOCAL_EGL_NATIVE_BUFFER_ANDROID,
-                                     aBuffer, usedAttrs);
+    return egl->fCreateImage(egl->Display(),
+                             EGL_NO_CONTEXT,
+                             LOCAL_EGL_NATIVE_BUFFER_ANDROID,
+                             aBuffer, usedAttrs);
 }
 
 void
 EGLImageDestroy(GLContext* aGL, EGLImage aImage)
 {
-    sEGLLibrary.fDestroyImage(sEGLLibrary.Display(), aImage);
+    auto* egl = gl::GLLibraryEGL::Get();
+    egl->fDestroyImage(egl->Display(), aImage);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -651,21 +651,22 @@ EGLImageTextureHost::gl() const
 bool
 EGLImageTextureHost::Lock()
 {
   GLContext* gl = this->gl();
   if (!gl || !gl->MakeCurrent()) {
     return false;
   }
 
+  auto* egl = gl::GLLibraryEGL::Get();
   EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
 
   if (mSync) {
-    MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
-    status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), mSync, 0, LOCAL_EGL_FOREVER);
+    MOZ_ASSERT(egl->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
+    status = egl->fClientWaitSync(EGL_DISPLAY(), mSync, 0, LOCAL_EGL_FOREVER);
   }
 
   if (status != LOCAL_EGL_CONDITION_SATISFIED) {
     MOZ_ASSERT(status != 0,
                "ClientWaitSync generated an error. Has mSync already been destroyed?");
     return false;
   }
 
--- a/gfx/vr/gfxVRGVR.cpp
+++ b/gfx/vr/gfxVRGVR.cpp
@@ -306,20 +306,21 @@ VRDisplayGVR::SubmitFrame(const mozilla:
     // issue.
     GVR_LOG("Unable to acquire GVR frame. Recreating swap chain.");
     RecreateSwapChain();
     return false;
   }
   GVR_CHECK(gvr_frame_bind_buffer(frame, 0));
 
   EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   if (sync) {
-    MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
-    status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), sync, 0, LOCAL_EGL_FOREVER);
+    MOZ_ASSERT(egl->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
+    status = egl->fClientWaitSync(EGL_DISPLAY(), sync, 0, LOCAL_EGL_FOREVER);
   }
 
   if (status != LOCAL_EGL_CONDITION_SATISFIED) {
     MOZ_ASSERT(status != 0,
                "ClientWaitSync generated an error. Has sync already been destroyed?");
     return false;
   }
 
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -48,17 +48,17 @@ RenderCompositorANGLE::~RenderCompositor
 {
   DestroyEGLSurface();
   MOZ_ASSERT(!mEGLSurface);
 }
 
 ID3D11Device*
 RenderCompositorANGLE::GetDeviceOfEGLDisplay()
 {
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   // Fetch the D3D11 device.
   EGLDeviceEXT eglDevice = nullptr;
   egl->fQueryDisplayAttribEXT(egl->Display(), LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
   MOZ_ASSERT(eglDevice);
   ID3D11Device* device = nullptr;
   egl->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE, (EGLAttrib*)&device);
   if (!device) {
@@ -66,24 +66,23 @@ RenderCompositorANGLE::GetDeviceOfEGLDis
     return nullptr;
   }
   return device;
 }
 
 bool
 RenderCompositorANGLE::Initialize()
 {
-  const auto& egl = &gl::sEGLLibrary;
-
   nsCString discardFailureId;
-  if (!egl->EnsureInitialized(/* forceAccel */ true, &discardFailureId)) {
+  if (!gl::GLLibraryEGL::EnsureInitialized(/* forceAccel */ true, &discardFailureId)) {
     gfxCriticalNote << "Failed to load EGL library: " << discardFailureId.get();
     return false;
   }
 
+  auto* egl = gl::GLLibraryEGL::Get();
   mDevice = GetDeviceOfEGLDisplay();
 
   if (!mDevice) {
     gfxCriticalNote << "[D3D11] failed to get compositor device.";
     return false;
   }
 
   mDevice->GetImmediateContext(getter_AddRefs(mCtx));
@@ -363,17 +362,17 @@ RenderCompositorANGLE::ResizeBufferIfNee
   if (hr == DXGI_ERROR_INVALID_CALL) {
     // This happens on some GPUs/drivers when there's a TDR.
     if (mDevice->GetDeviceRemovedReason() != S_OK) {
       gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr) << " Size : " << size;
       return false;
     }
   }
 
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   const EGLint pbuffer_attribs[]{
     LOCAL_EGL_WIDTH, size.width,
     LOCAL_EGL_HEIGHT, size.height,
     LOCAL_EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, LOCAL_EGL_TRUE,
     LOCAL_EGL_NONE};
 
   const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
@@ -394,17 +393,17 @@ RenderCompositorANGLE::ResizeBufferIfNee
   mBufferSize = Some(size);
 
   return true;
 }
 
 void
 RenderCompositorANGLE::DestroyEGLSurface()
 {
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   // Release EGLSurface of back buffer before calling ResizeBuffers().
   if (mEGLSurface) {
     gl::GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(EGL_NO_SURFACE);
     egl->fDestroySurface(egl->Display(), mEGLSurface);
     mEGLSurface = nullptr;
   }
 }
--- a/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp
+++ b/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp
@@ -62,17 +62,17 @@ RenderDXGITextureHostOGL::~RenderDXGITex
 
 bool
 RenderDXGITextureHostOGL::EnsureLockable()
 {
   if (mTextureHandle[0]) {
     return true;
   }
 
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   // We use EGLStream to get the converted gl handle from d3d texture. The
   // NV_stream_consumer_gltexture_yuv and ANGLE_stream_producer_d3d_texture
   // could support nv12 and rgb d3d texture format.
   if (!egl->IsExtensionSupported(gl::GLLibraryEGL::NV_stream_consumer_gltexture_yuv) ||
       !egl->IsExtensionSupported(gl::GLLibraryEGL::ANGLE_stream_producer_d3d_texture)) {
     return false;
   }
@@ -198,17 +198,17 @@ RenderDXGITextureHostOGL::DeleteTextureH
 
   if (mGL && mGL->MakeCurrent()) {
     mGL->fDeleteTextures(2, mTextureHandle);
   }
   for(int i = 0; i < 2; ++i) {
     mTextureHandle[i] = 0;
   }
 
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
   if (mSurface) {
     egl->fDestroySurface(egl->Display(), mSurface);
     mSurface = 0;
   }
   if (mStream) {
     egl->fDestroyStreamKHR(egl->Display(), mStream);
     mStream = 0;
   }
@@ -268,17 +268,17 @@ RenderDXGIYCbCrTextureHostOGL::~RenderDX
 
 bool
 RenderDXGIYCbCrTextureHostOGL::EnsureLockable()
 {
   if (mTextureHandles[0]) {
     return true;
   }
 
-  const auto& egl = &gl::sEGLLibrary;
+  auto* egl = gl::GLLibraryEGL::Get();
 
   // The eglCreatePbufferFromClientBuffer doesn't support R8 format, so we
   // use EGLStream to get the converted gl handle from d3d R8 texture.
 
   if (!egl->IsExtensionSupported(gl::GLLibraryEGL::NV_stream_consumer_gltexture_yuv) ||
       !egl->IsExtensionSupported(gl::GLLibraryEGL::ANGLE_stream_producer_d3d_texture))
   {
       return false;
@@ -408,17 +408,17 @@ RenderDXGIYCbCrTextureHostOGL::DeleteTex
   if (mGL && mGL->MakeCurrent()) {
     mGL->fDeleteTextures(3, mTextureHandles);
   }
   for (int i = 0; i < 3; ++i) {
     mTextureHandles[i] = 0;
     mTextures[i] = nullptr;
     mKeyedMutexs[i] = nullptr;
 
-    const auto& egl = &gl::sEGLLibrary;
+    auto* egl = gl::GLLibraryEGL::Get();
     if (mSurfaces[i]) {
       egl->fDestroySurface(egl->Display(), mSurfaces[i]);
       mSurfaces[i] = 0;
     }
     if (mStreams[i]) {
       egl->fDestroyStreamKHR(egl->Display(), mStreams[i]);
       mStreams[i] = 0;
     }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -567,29 +567,16 @@ WasmThreadsSupported(JSContext* cx, unsi
 #else
     bool isSupported = false;
 #endif
     args.rval().setBoolean(isSupported);
     return true;
 }
 
 static bool
-WasmSignExtensionSupported(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
-    bool isSupported = true;
-#else
-    bool isSupported = false;
-#endif
-    args.rval().setBoolean(isSupported);
-    return true;
-}
-
-static bool
 WasmSaturatingTruncationSupported(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
     bool isSupported = true;
 #else
     bool isSupported = false;
 #endif
@@ -5558,21 +5545,16 @@ gc::ZealModeHelpText),
 "  Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
 "  returns false also if WebAssembly is not supported"),
 
     JS_FN_HELP("wasmThreadsSupported", WasmThreadsSupported, 0, 0,
 "wasmThreadsSupported()",
 "  Returns a boolean indicating whether the WebAssembly threads proposal is\n"
 "  supported on the current device."),
 
-    JS_FN_HELP("wasmSignExtensionSupported", WasmSignExtensionSupported, 0, 0,
-"wasmSignExtensionSupported()",
-"  Returns a boolean indicating whether the WebAssembly sign extension opcodes are\n"
-"  supported on the current device."),
-
     JS_FN_HELP("wasmSaturatingTruncationSupported", WasmSaturatingTruncationSupported, 0, 0,
 "wasmSaturatingTruncationSupported()",
 "  Returns a boolean indicating whether the WebAssembly saturating truncates opcodes are\n"
 "  supported on the current device."),
 
     JS_FN_HELP("wasmBulkMemSupported", WasmBulkMemSupported, 0, 0,
 "wasmBulkMemSupported()",
 "  Returns a boolean indicating whether the WebAssembly bulk memory proposal is\n"
--- a/js/src/gc/AtomMarking.cpp
+++ b/js/src/gc/AtomMarking.cpp
@@ -24,22 +24,22 @@ namespace gc {
 // for each zone that is always an overapproximation of the atoms that zone is
 // using. When an atom is not in the mark bitmap for any zone, it can be
 // destroyed.
 //
 // To minimize interference with the rest of the GC, atom marking and sweeping
 // is done by manipulating the mark bitmaps in the chunks used for the atoms.
 // When the atoms zone is being collected, the mark bitmaps for the chunk(s)
 // used by the atoms are updated normally during marking. After marking
-// finishes, the chunk mark bitmaps are translated to a more efficient atom
-// mark bitmap (see below) that is stored on the zones which the GC collected
+// finishes, the chunk mark bitmaps are translated to a more efficient atom mark
+// bitmap (see below) that is stored on the zones which the GC collected
 // (computeBitmapFromChunkMarkBits). Before sweeping begins, the chunk mark
 // bitmaps are updated with any atoms that might be referenced by zones which
-// weren't collected (updateChunkMarkBits). The GC sweeping will then release
-// all atoms which are not marked by any zone.
+// weren't collected (markAtomsUsedByUncollectedZones). The GC sweeping will
+// then release all atoms which are not marked by any zone.
 //
 // The representation of atom mark bitmaps is as follows:
 //
 // Each arena in the atoms zone has an atomBitmapStart() value indicating the
 // word index into the bitmap of the first thing in the arena. Each arena uses
 // ArenaBitmapWords of data to store its bitmap, which uses the same
 // representation as chunk mark bitmaps: one bit is allocated per Cell, with
 // bits for space between things being unused when things are larger than a
@@ -91,31 +91,33 @@ AtomMarkingRuntime::computeBitmapFromChu
             bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 
     return true;
 }
 
 void
-AtomMarkingRuntime::updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap)
+AtomMarkingRuntime::refineZoneBitmapForCollectedZone(Zone* zone, const DenseBitmap& bitmap)
 {
+    MOZ_ASSERT(zone->isCollectingFromAnyThread());
+
     if (zone->isAtomsZone())
         return;
 
     // Take the bitwise and between the two mark bitmaps to get the best new
     // overapproximation we can. |bitmap| might include bits that are not in
     // the zone's mark bitmap, if additional zones were collected by the GC.
     zone->markedAtoms().bitwiseAndWith(bitmap);
 }
 
 // Set any bits in the chunk mark bitmaps for atoms which are marked in bitmap.
 template <typename Bitmap>
 static void
-AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
+BitwiseOrIntoChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
 {
     // Make sure that by copying the mark bits for one arena in word sizes we
     // do not affect the mark bits for other arenas.
     static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
                   "ArenaBitmapWords must evenly divide ArenaBitmapBits");
 
     Zone* atomsZone = runtime->unsafeAtomsZone();
     for (auto thingKind : AllAllocKinds()) {
@@ -123,38 +125,38 @@ AddBitmapToChunkMarkBits(JSRuntime* runt
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
             bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 }
 
 void
-AtomMarkingRuntime::updateChunkMarkBits(JSRuntime* runtime)
+AtomMarkingRuntime::markAtomsUsedByUncollectedZones(JSRuntime* runtime)
 {
     MOZ_ASSERT(CurrentThreadIsPerformingGC());
     MOZ_ASSERT(!runtime->hasHelperThreadZones());
 
     // Try to compute a simple union of the zone atom bitmaps before updating
     // the chunk mark bitmaps. If this allocation fails then fall back to
     // updating the chunk mark bitmaps separately for each zone.
     DenseBitmap markedUnion;
     if (markedUnion.ensureSpace(allocatedWords)) {
         for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
             // We only need to update the chunk mark bits for zones which were
             // not collected in the current GC. Atoms which are referenced by
             // collected zones have already been marked.
             if (!zone->isCollectingFromAnyThread())
                 zone->markedAtoms().bitwiseOrInto(markedUnion);
         }
-        AddBitmapToChunkMarkBits(runtime, markedUnion);
+        BitwiseOrIntoChunkMarkBits(runtime, markedUnion);
     } else {
         for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
             if (!zone->isCollectingFromAnyThread())
-                AddBitmapToChunkMarkBits(runtime, zone->markedAtoms());
+                BitwiseOrIntoChunkMarkBits(runtime, zone->markedAtoms());
         }
     }
 }
 
 template <typename T>
 void
 AtomMarkingRuntime::markAtom(JSContext* cx, T* thing)
 {
--- a/js/src/gc/AtomMarking.h
+++ b/js/src/gc/AtomMarking.h
@@ -48,21 +48,21 @@ class AtomMarkingRuntime
 
     // Fill |bitmap| with an atom marking bitmap based on the things that are
     // currently marked in the chunks used by atoms zone arenas. This returns
     // false on an allocation failure (but does not report an exception).
     bool computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap);
 
     // Update the atom marking bitmap in |zone| according to another
     // overapproximation of the reachable atoms in |bitmap|.
-    void updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap);
+    void refineZoneBitmapForCollectedZone(Zone* zone, const DenseBitmap& bitmap);
 
     // Set any bits in the chunk mark bitmaps for atoms which are marked in any
-    // zone in the runtime.
-    void updateChunkMarkBits(JSRuntime* runtime);
+    // uncollected zone in the runtime.
+    void markAtomsUsedByUncollectedZones(JSRuntime* runtime);
 
     // Mark an atom or id as being newly reachable by the context's zone.
     template <typename T> void markAtom(JSContext* cx, T* thing);
 
     // Version of markAtom that's always inlined, for performance-sensitive
     // callers.
     template <typename T> MOZ_ALWAYS_INLINE void inlinedMarkAtom(JSContext* cx, T* thing);
 
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -2854,32 +2854,40 @@ GCRuntime::updateCellPointers(Zone* zone
 //   - Updating a typed object makes use of its type descriptor object
 //
 // This means we require at least three phases for update:
 //
 //  1) shapes
 //  2) typed object type descriptor objects
 //  3) all other objects
 //
-// Since we want to minimize the number of phases, we put everything else into
-// the first phase and label it the 'misc' phase.
-
-static const AllocKinds UpdatePhaseMisc {
+// Also, JSScripts and LazyScripts can have pointers to each other. Each can be
+// updated safely without requiring the referent to be up-to-date, but TSAN can
+// warn about data races when calling IsForwarded() on the new location of a
+// cell that is being updated in parallel. To avoid this, we update these in
+// separate phases.
+//
+// Since we want to minimize the number of phases, arrange kinds into three
+// arbitrary phases.
+
+static const AllocKinds UpdatePhaseOne {
     AllocKind::SCRIPT,
-    AllocKind::LAZY_SCRIPT,
     AllocKind::BASE_SHAPE,
     AllocKind::SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::OBJECT_GROUP,
     AllocKind::STRING,
     AllocKind::JITCODE,
     AllocKind::SCOPE
 };
 
-static const AllocKinds UpdatePhaseObjects {
+// UpdatePhaseTwo is typed object descriptor objects.
+
+static const AllocKinds UpdatePhaseThree {
+    AllocKind::LAZY_SCRIPT,
     AllocKind::FUNCTION,
     AllocKind::FUNCTION_EXTENDED,
     AllocKind::OBJECT0,
     AllocKind::OBJECT0_BACKGROUND,
     AllocKind::OBJECT2,
     AllocKind::OBJECT2_BACKGROUND,
     AllocKind::OBJECT4,
     AllocKind::OBJECT4_BACKGROUND,
@@ -2891,23 +2899,23 @@ static const AllocKinds UpdatePhaseObjec
     AllocKind::OBJECT16_BACKGROUND
 };
 
 void
 GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
 {
     size_t bgTaskCount = CellUpdateBackgroundTaskCount();
 
-    updateCellPointers(zone, UpdatePhaseMisc, bgTaskCount);
-
-    // Update TypeDescrs before all other objects as typed objects access these
-    // objects when we trace them.
+    updateCellPointers(zone, UpdatePhaseOne, bgTaskCount);
+
+    // UpdatePhaseTwo: Update TypeDescrs before all other objects as typed
+    // objects access these objects when we trace them.
     updateTypeDescrObjects(trc, zone);
 
-    updateCellPointers(zone, UpdatePhaseObjects, bgTaskCount);
+    updateCellPointers(zone, UpdatePhaseThree, bgTaskCount);
 }
 
 /*
  * Update pointers to relocated cells in a single zone by doing a traversal of
  * that zone's arenas and calling per-zone sweep hooks.
  *
  * The latter is necessary to update weak references which are not marked as
  * part of the traversal.
@@ -5385,31 +5393,29 @@ class ImmediateSweepWeakCacheTask : publ
     {}
 
     void run() {
         cache.sweep();
     }
 };
 
 static void
-UpdateAtomsBitmap(GCParallelTask* task)
-{
-    JSRuntime* runtime = task->runtime();
-
+UpdateAtomsBitmap(JSRuntime* runtime)
+{
     DenseBitmap marked;
     if (runtime->gc.atomMarking.computeBitmapFromChunkMarkBits(runtime, marked)) {
         for (GCZonesIter zone(runtime); !zone.done(); zone.next())
-            runtime->gc.atomMarking.updateZoneBitmap(zone, marked);
+            runtime->gc.atomMarking.refineZoneBitmapForCollectedZone(zone, marked);
     } else {
-        // Ignore OOM in computeBitmapFromChunkMarkBits. The updateZoneBitmap
-        // call can only remove atoms from the zone bitmap, so it is
-        // conservative to just not call it.
-    }
-
-    runtime->gc.atomMarking.updateChunkMarkBits(runtime);
+        // Ignore OOM in computeBitmapFromChunkMarkBits. The
+        // refineZoneBitmapForCollectedZone call can only remove atoms from the
+        // zone bitmap, so it is conservative to just not call it.
+    }
+
+    runtime->gc.atomMarking.markAtomsUsedByUncollectedZones(runtime);
 
     // For convenience sweep these tables non-incrementally as part of bitmap
     // sweeping; they are likely to be much smaller than the main atoms table.
     runtime->unsafeSymbolRegistry().sweep();
     for (RealmsIter realm(runtime); !realm.done(); realm.next())
         realm->sweepVarNames();
 }
 
@@ -5702,25 +5708,29 @@ GCRuntime::beginSweepingSweepGroup(FreeO
             for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
                 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                     callWeakPointerCompartmentCallbacks(comp);
             }
         }
         callFinalizeCallbacks(fop, JSFINALIZE_GROUP_START);
     }
 
+    // Updating the atom marking bitmaps. This marks atoms referenced by
+    // uncollected zones so cannot be done in parallel with the other sweeping
+    // work below.
+    if (sweepingAtoms) {
+        AutoPhase ap(stats(), PhaseKind::UPDATE_ATOMS_BITMAP);
+        UpdateAtomsBitmap(rt);
+    }
+
     sweepDebuggerOnMainThread(fop);
 
     {
         AutoLockHelperThreadState lock;
 
-        Maybe<AutoRunParallelTask> updateAtomsBitmap;
-        if (sweepingAtoms)
-            updateAtomsBitmap.emplace(rt, UpdateAtomsBitmap, PhaseKind::UPDATE_ATOMS_BITMAP, lock);
-
         AutoPhase ap(stats(), PhaseKind::SWEEP_COMPARTMENTS);
 
         AutoRunParallelTask sweepCCWrappers(rt, SweepCCWrappers, PhaseKind::SWEEP_CC_WRAPPER, lock);
         AutoRunParallelTask sweepObjectGroups(rt, SweepObjectGroups, PhaseKind::SWEEP_TYPE_OBJECT, lock);
         AutoRunParallelTask sweepMisc(rt, SweepMisc, PhaseKind::SWEEP_MISC, lock);
         AutoRunParallelTask sweepCompTasks(rt, SweepCompressionTasks, PhaseKind::SWEEP_COMPRESSION, lock);
         AutoRunParallelTask sweepWeakMaps(rt, SweepWeakMaps, PhaseKind::SWEEP_WEAKMAPS, lock);
         AutoRunParallelTask sweepUniqueIds(rt, SweepUniqueIds, PhaseKind::SWEEP_UNIQUEIDS, lock);
--- a/js/src/jit-test/tests/wasm/conversion.js
+++ b/js/src/jit-test/tests/wasm/conversion.js
@@ -291,28 +291,26 @@ if (wasmSaturatingTruncationSupported())
 
     testConversion('i64', 'trunc_u:sat', 'f32', 18446744073709551616.0, u64max);
     testConversion('i64', 'trunc_u:sat', 'f32', -1, '0');
     testConversion('i64', 'trunc_u:sat', 'f32', 'nan', '0');
     testConversion('i64', 'trunc_u:sat', 'f32', 'infinity', u64max);
     testConversion('i64', 'trunc_u:sat', 'f32', '-infinity', '0');
 }
 
-if (wasmSignExtensionSupported()) {
-    testSignExtension('i32', 'extend8_s', 'i32', 0x7F, 0x7F);
-    testSignExtension('i32', 'extend8_s', 'i32', 0x80, -0x80);
-    testSignExtension('i32', 'extend16_s', 'i32', 0x7FFF, 0x7FFF);
-    testSignExtension('i32', 'extend16_s', 'i32', 0x8000, -0x8000);
-    testSignExtension('i64', 'extend8_s', 'i64', 0x7F, 0x7F);
-    testSignExtension('i64', 'extend8_s', 'i64', 0x80, -0x80);
-    testSignExtension('i64', 'extend16_s', 'i64', 0x7FFF, 0x7FFF);
-    testSignExtension('i64', 'extend16_s', 'i64', 0x8000, -0x8000);
-    testSignExtension('i64', 'extend32_s', 'i64', 0x7FFFFFFF, 0x7FFFFFFF);
-    testSignExtension('i64', 'extend32_s', 'i64', "0x80000000", "0xFFFFFFFF80000000");
-}
+testSignExtension('i32', 'extend8_s', 'i32', 0x7F, 0x7F);
+testSignExtension('i32', 'extend8_s', 'i32', 0x80, -0x80);
+testSignExtension('i32', 'extend16_s', 'i32', 0x7FFF, 0x7FFF);
+testSignExtension('i32', 'extend16_s', 'i32', 0x8000, -0x8000);
+testSignExtension('i64', 'extend8_s', 'i64', 0x7F, 0x7F);
+testSignExtension('i64', 'extend8_s', 'i64', 0x80, -0x80);
+testSignExtension('i64', 'extend16_s', 'i64', 0x7FFF, 0x7FFF);
+testSignExtension('i64', 'extend16_s', 'i64', 0x8000, -0x8000);
+testSignExtension('i64', 'extend32_s', 'i64', 0x7FFFFFFF, 0x7FFFFFFF);
+testSignExtension('i64', 'extend32_s', 'i64', "0x80000000", "0xFFFFFFFF80000000");
 
 // i32.trunc_s* : all values in ] -2**31 - 1; 2**31 [ are acceptable.
 // f32:
 testConversion('i32', 'trunc_s', 'f32', 40.1, 40);
 testConversion('i32', 'trunc_s', 'f32', p(2, 31) - 128, p(2, 31) - 128); // last f32 value exactly representable < 2**31.
 testConversion('i32', 'trunc_s', 'f32', -p(2, 31), -p(2,31)); // last f32 value exactly representable > -2**31 - 1.
 
 testTrap('i32', 'trunc_s', 'f32', 'nan');
--- a/js/src/jit-test/tests/wasm/spec/extend32.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/extend32.wast.js
@@ -1,11 +1,8 @@
-if (!wasmSignExtensionSupported())
-    quit(0);
-
 // extend32.wast:3
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7f\x03\x83\x80\x80\x80\x00\x02\x00\x00\x07\x9a\x80\x80\x80\x00\x02\x09\x65\x78\x74\x65\x6e\x64\x38\x5f\x73\x00\x00\x0a\x65\x78\x74\x65\x6e\x64\x31\x36\x5f\x73\x00\x01\x0a\x95\x80\x80\x80\x00\x02\x85\x80\x80\x80\x00\x00\x20\x00\xc0\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xc1\x0b");
 
 // extend32.wast:8
 assert_return(() => call($1, "extend8_s", [0]), 0);
 
 // extend32.wast:9
 assert_return(() => call($1, "extend8_s", [127]), 127);
--- a/js/src/jit-test/tests/wasm/spec/extend64.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/extend64.wast.js
@@ -1,11 +1,8 @@
-if (!wasmSignExtensionSupported())
-    quit(0);
-
 // extend64.wast:3
 let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7e\x01\x7e\x03\x84\x80\x80\x80\x00\x03\x00\x00\x00\x07\xa7\x80\x80\x80\x00\x03\x09\x65\x78\x74\x65\x6e\x64\x38\x5f\x73\x00\x00\x0a\x65\x78\x74\x65\x6e\x64\x31\x36\x5f\x73\x00\x01\x0a\x65\x78\x74\x65\x6e\x64\x33\x32\x5f\x73\x00\x02\x0a\x9f\x80\x80\x80\x00\x03\x85\x80\x80\x80\x00\x00\x20\x00\xc2\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xc3\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xc4\x0b");
 
 // extend64.wast:9
 run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x00\x60\x01\x7e\x01\x7e\x02\x90\x80\x80\x80\x00\x01\x02\x24\x31\x09\x65\x78\x74\x65\x6e\x64\x38\x5f\x73\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x99\x80\x80\x80\x00\x01\x93\x80\x80\x80\x00\x00\x02\x40\x42\x00\x10\x00\x01\x42\x00\x01\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "extend8_s", [int64("0")]), int64("0"))
 
 // extend64.wast:10
 run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x89\x80\x80\x80\x00\x02\x60\x00\x00\x60\x01\x7e\x01\x7e\x02\x90\x80\x80\x80\x00\x01\x02\x24\x31\x09\x65\x78\x74\x65\x6e\x64\x38\x5f\x73\x00\x01\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x01\x0a\x9b\x80\x80\x80\x00\x01\x95\x80\x80\x80\x00\x00\x02\x40\x42\xff\x00\x10\x00\x01\x42\xff\x00\x01\x51\x45\x0d\x00\x0f\x0b\x00\x0b", exports("$1", $1)),  "run", []));  // assert_return(() => call($1, "extend8_s", [int64("127")]), int64("127"))
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -692,17 +692,16 @@ DIRS += [
 
 FINAL_LIBRARY = 'js'
 
 if CONFIG['NIGHTLY_BUILD']:
     DEFINES['ENABLE_BINARYDATA'] = True
     DEFINES['ENABLE_SIMD'] = True
     DEFINES['ENABLE_WASM_BULKMEM_OPS'] = True
     DEFINES['ENABLE_WASM_SATURATING_TRUNC_OPS'] = True
-    DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
     DEFINES['ENABLE_WASM_THREAD_OPS'] = True
     DEFINES['ENABLE_WASM_GC'] = True
 
 if CONFIG['JS_BUILD_BINAST']:
     # Using SOURCES, as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.
     # These parts of BinAST are designed only to test evolutions of the
     # specification.
     SOURCES += ['frontend/BinTokenReaderTester.cpp']
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -9774,28 +9774,26 @@ BaseCompiler::emitBody()
           case uint16_t(Op::F64Le):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThanOrEqual));
           case uint16_t(Op::F64Gt):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThan));
           case uint16_t(Op::F64Ge):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThanOrEqual));
 
           // Sign extensions
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
           case uint16_t(Op::I32Extend8S):
             CHECK_NEXT(emitConversion(emitExtendI32_8, ValType::I32, ValType::I32));
           case uint16_t(Op::I32Extend16S):
             CHECK_NEXT(emitConversion(emitExtendI32_16, ValType::I32, ValType::I32));
           case uint16_t(Op::I64Extend8S):
             CHECK_NEXT(emitConversion(emitExtendI64_8, ValType::I64, ValType::I64));
           case uint16_t(Op::I64Extend16S):
             CHECK_NEXT(emitConversion(emitExtendI64_16, ValType::I64, ValType::I64));
           case uint16_t(Op::I64Extend32S):
             CHECK_NEXT(emitConversion(emitExtendI64_32, ValType::I64, ValType::I64));
-#endif
 
           // Memory Related
           case uint16_t(Op::GrowMemory):
             CHECK_NEXT(emitGrowMemory());
           case uint16_t(Op::CurrentMemory):
             CHECK_NEXT(emitCurrentMemory());
 
 #ifdef ENABLE_WASM_GC
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -294,24 +294,22 @@ enum class Op
     F64PromoteF32                        = 0xbb,
 
     // Reinterpretations
     I32ReinterpretF32                    = 0xbc,
     I64ReinterpretF64                    = 0xbd,
     F32ReinterpretI32                    = 0xbe,
     F64ReinterpretI64                    = 0xbf,
 
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
     // Sign extension
     I32Extend8S                          = 0xc0,
     I32Extend16S                         = 0xc1,
     I64Extend8S                          = 0xc2,
     I64Extend16S                         = 0xc3,
     I64Extend32S                         = 0xc4,
-#endif
 
     // GC ops
     RefNull                              = 0xd0,
     RefIsNull                            = 0xd1,
 
     FirstPrefix                          = 0xfc,
     MiscPrefix                           = 0xfc,
     ThreadPrefix                         = 0xfe,
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1579,29 +1579,27 @@ AstDecodeExpr(AstDecodeContext& c)
       case uint16_t(Op::F64ReinterpretI64):
         if (!AstDecodeConversion(c, ValType::I64, ValType::F64, Op(op.b0)))
             return false;
         break;
       case uint16_t(Op::F64PromoteF32):
         if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op.b0)))
             return false;
         break;
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
       case uint16_t(Op::I32Extend8S):
       case uint16_t(Op::I32Extend16S):
         if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0)))
             return false;
         break;
       case uint16_t(Op::I64Extend8S):
       case uint16_t(Op::I64Extend16S):
       case uint16_t(Op::I64Extend32S):
         if (!AstDecodeConversion(c, ValType::I64, ValType::I64, Op(op.b0)))
             return false;
         break;
-#endif
       case uint16_t(Op::I32Load8S):
       case uint16_t(Op::I32Load8U):
         if (!AstDecodeLoad(c, ValType::I32, 1, Op(op.b0)))
             return false;
         break;
       case uint16_t(Op::I32Load16S):
       case uint16_t(Op::I32Load16U):
         if (!AstDecodeLoad(c, ValType::I32, 2, Op(op.b0)))
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -730,23 +730,21 @@ RenderConversionOperator(WasmRenderConte
       case Op::F32ConvertUI64:    opStr = "f32.convert_u/i64"; break;
       case Op::F32DemoteF64:      opStr = "f32.demote/f64"; break;
       case Op::F64ConvertSI32:    opStr = "f64.convert_s/i32"; break;
       case Op::F64ConvertUI32:    opStr = "f64.convert_u/i32"; break;
       case Op::F64ConvertSI64:    opStr = "f64.convert_s/i64"; break;
       case Op::F64ConvertUI64:    opStr = "f64.convert_u/i64"; break;
       case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
       case Op::F64PromoteF32:     opStr = "f64.promote/f32"; break;
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
       case Op::I32Extend8S:       opStr = "i32.extend8_s"; break;
       case Op::I32Extend16S:      opStr = "i32.extend16_s"; break;
       case Op::I64Extend8S:       opStr = "i64.extend8_s"; break;
       case Op::I64Extend16S:      opStr = "i64.extend16_s"; break;
       case Op::I64Extend32S:      opStr = "i64.extend32_s"; break;
-#endif
       case Op::I32Eqz:            opStr = "i32.eqz"; break;
       case Op::I64Eqz:            opStr = "i64.eqz"; break;
       default:                      return Fail(c, "unexpected conversion operator");
     }
     return c.buffer.append(opStr, strlen(opStr));
 }
 
 #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -2437,29 +2437,27 @@ EmitTruncate(FunctionCompiler& f, ValTyp
     } else {
         MOZ_ASSERT(resultType == ValType::I64);
         MOZ_ASSERT(!f.env().isAsmJS());
         f.iter().setResult(f.truncate<MWasmTruncateToInt64>(input, flags));
     }
     return true;
 }
 
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
 static bool
 EmitSignExtend(FunctionCompiler& f, uint32_t srcSize, uint32_t targetSize)
 {
     MDefinition* input;
     ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
     if (!f.iter().readConversion(type, type, &input))
         return false;
 
     f.iter().setResult(f.signExtend(input, srcSize, targetSize));
     return true;
 }
-#endif
 
 static bool
 EmitExtendI32(FunctionCompiler& f, bool isUnsigned)
 {
     MDefinition* input;
     if (!f.iter().readConversion(ValType::I32, ValType::I64, &input))
         return false;
 
@@ -4059,28 +4057,26 @@ EmitBodyExprs(FunctionCompiler& f)
             CHECK(EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double));
 
           // GC types are NYI in Ion.
           case uint16_t(Op::RefNull):
           case uint16_t(Op::RefIsNull):
             return f.iter().unrecognizedOpcode(&op);
 
           // Sign extensions
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
           case uint16_t(Op::I32Extend8S):
             CHECK(EmitSignExtend(f, 1, 4));
           case uint16_t(Op::I32Extend16S):
             CHECK(EmitSignExtend(f, 2, 4));
           case uint16_t(Op::I64Extend8S):
             CHECK(EmitSignExtend(f, 1, 8));
           case uint16_t(Op::I64Extend16S):
             CHECK(EmitSignExtend(f, 2, 8));
           case uint16_t(Op::I64Extend32S):
             CHECK(EmitSignExtend(f, 4, 8));
-#endif
 
           // Miscellaneous operations
           case uint16_t(Op::MiscPrefix): {
             switch (op.b1) {
 #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
               case uint16_t(MiscOp::I32TruncSSatF32):
               case uint16_t(MiscOp::I32TruncUSatF32):
                 CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
--- a/js/src/wasm/WasmOpIter.cpp
+++ b/js/src/wasm/WasmOpIter.cpp
@@ -173,23 +173,21 @@ wasm::Classify(OpBytes op)
       case Op::F32DemoteF64:
       case Op::F64ConvertSI32:
       case Op::F64ConvertUI32:
       case Op::F64ConvertSI64:
       case Op::F64ConvertUI64:
       case Op::F64ReinterpretI64:
       case Op::F64PromoteF32:
       case Op::RefIsNull:
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
       case Op::I32Extend8S:
       case Op::I32Extend16S:
       case Op::I64Extend8S:
       case Op::I64Extend16S:
       case Op::I64Extend32S:
-#endif
         return OpKind::Conversion;
       case Op::I32Load8S:
       case Op::I32Load8U:
       case Op::I32Load16S:
       case Op::I32Load16U:
       case Op::I64Load8S:
       case Op::I64Load8U:
       case Op::I64Load16S:
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -1255,22 +1255,20 @@ WasmTokenStream::next()
                 if (consume(u"div_u"))
                     return WasmToken(WasmToken::BinaryOpcode, Op::I32DivU, begin, cur_);
                 break;
               case 'e':
                 if (consume(u"eqz"))
                     return WasmToken(WasmToken::UnaryOpcode, Op::I32Eqz, begin, cur_);
                 if (consume(u"eq"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32Eq, begin, cur_);
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
                 if (consume(u"extend8_s"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend8S, begin, cur_);
                 if (consume(u"extend16_s"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend16S, begin, cur_);
-#endif
                 break;
               case 'g':
                 if (consume(u"ge_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GeS, begin, cur_);
                 if (consume(u"ge_u"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GeU, begin, cur_);
                 if (consume(u"gt_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GtS, begin, cur_);
@@ -1497,24 +1495,22 @@ WasmTokenStream::next()
                 if (consume(u"eq"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64Eq, begin, cur_);
                 if (consume(u"extend_s/i32"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64ExtendSI32,
                                      begin, cur_);
                 if (consume(u"extend_u/i32"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64ExtendUI32,
                                      begin, cur_);
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
                 if (consume(u"extend8_s"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend8S, begin, cur_);
                 if (consume(u"extend16_s"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend16S, begin, cur_);
                 if (consume(u"extend32_s"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend32S, begin, cur_);
-#endif
                 break;
               case 'g':
                 if (consume(u"ge_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GeS, begin, cur_);
                 if (consume(u"ge_u"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GeU, begin, cur_);
                 if (consume(u"gt_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GtS, begin, cur_);
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -686,25 +686,23 @@ DecodeFunctionBodyExprs(const ModuleEnvi
           case uint16_t(Op::F64ConvertUI32):
             CHECK(iter.readConversion(ValType::I32, ValType::F64, &nothing));
           case uint16_t(Op::F64ConvertSI64):
           case uint16_t(Op::F64ConvertUI64):
           case uint16_t(Op::F64ReinterpretI64):
             CHECK(iter.readConversion(ValType::I64, ValType::F64, &nothing));
           case uint16_t(Op::F64PromoteF32):
             CHECK(iter.readConversion(ValType::F32, ValType::F64, &nothing));
-#ifdef ENABLE_WASM_SIGNEXTEND_OPS
           case uint16_t(Op::I32Extend8S):
           case uint16_t(Op::I32Extend16S):
             CHECK(iter.readConversion(ValType::I32, ValType::I32, &nothing));
           case uint16_t(Op::I64Extend8S):
           case uint16_t(Op::I64Extend16S):
           case uint16_t(Op::I64Extend32S):
             CHECK(iter.readConversion(ValType::I64, ValType::I64, &nothing));
-#endif
           case uint16_t(Op::I32Load8S):
           case uint16_t(Op::I32Load8U): {
             LinearMemoryAddress<Nothing> addr;
             CHECK(iter.readLoad(ValType::I32, 1, &addr));
           }
           case uint16_t(Op::I32Load16S):
           case uint16_t(Op::I32Load16U): {
             LinearMemoryAddress<Nothing> addr;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-matching/descriptor-ranges-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+
+<style id="dynamicStyles">
+</style>
+
+<div id="testContents" style="columns: 2; font-size: 10px;">
+</div>
+
+<script>
+/* Reference file creates only a single face in each test font-family,
+   so that it will always have the expected rendering without relying
+   on the font-matching algorithm to choose between faces. */
+function createFontFaceRules(family, descriptor, expectedMatch, unexpectedMatch) {
+    return `@font-face { font-family: ${family}; src: url(../fonts/Chunkfive.otf); ${descriptor}: ${expectedMatch}; }\n`;
+}
+</script>
+
+<script src="descriptor-ranges.js"></script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-matching/descriptor-ranges.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+
+<style id="dynamicStyles">
+</style>
+
+<div id="testContents" style="columns: 2; font-size: 10px;">
+</div>
+
+<script>
+/* Create font-family with two faces; the testcase to use the face with the
+   'expectedMatch' descriptor, and not the 'unexpectedMatch'. So any testcase
+   whose text is replaced by Ahem glyphs has failed font-matching. */
+function createFontFaceRules(family, descriptor, expectedMatch, unexpectedMatch) {
+    return `@font-face { font-family: ${family}; src: url(../fonts/Chunkfive.otf); ${descriptor}: ${expectedMatch}; }\n` +
+           `@font-face { font-family: ${family}; src: url(../fonts/Ahem.ttf); ${descriptor}: ${unexpectedMatch}; }\n`;
+}
+</script>
+
+<script src="descriptor-ranges.js"></script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-matching/descriptor-ranges.js
@@ -0,0 +1,79 @@
+/* Script used by descriptor-ranges.html and descriptor-ranges-ref.html
+   to create @font-face rules and test elements for a collection of
+   font-matching testcases. */
+
+// To create unique font-family names for each testcase.
+let serial = 0;
+
+// Accumulators for the lists of @font-face rules and test elements.
+let fontFaceRules = "";
+let testElements = "";
+
+// Create a <div> element with the font properties to match. Its text reports
+// the property-value and corresponding pair of descriptors being tested.
+// (The associated createFontFaceRules() function is defined separately in the
+// test and reference files.)
+function createTestElement(family, weight, style, stretch, value, expected, unexpected) {
+    return `<div style="font-family:${family}; font-weight:${weight}; font-style:${style}; font-stretch:${stretch};">` +
+           `${family} ${value} (${expected} vs ${unexpected})</div>\n`;
+}
+
+// Create testcases for the given descriptor.
+// Each testcase has a test property value, and a list of @font-face descriptors
+// to be matched against the property, where each descriptor in the list should
+// be preferred over the next.
+function testDescriptor(descriptorName, testCases) {
+    testElements += `<div style="background:yellow;padding:0.5em">Tests of ${descriptorName} descriptor:</div>\n`;
+    testCases.forEach(function (testCase) {
+        // Go though test cases, checking each descriptor has higher priority than next in the list
+        for (let i = 0; i < testCase.testDescriptors.length - 1; i++) {
+            serial++;
+            let expectedMatch   = testCase.testDescriptors[i];
+            let unexpectedMatch = testCase.testDescriptors[i + 1];
+            let familyName = "test_" + serial;
+            fontFaceRules += createFontFaceRules(familyName, descriptorName, expectedMatch, unexpectedMatch);
+            let testWeight  = (descriptorName == "font-weight")  ? testCase.value : "normal";
+            let testStyle   = (descriptorName == "font-style")   ? testCase.value : "normal";
+            let testStretch = (descriptorName == "font-stretch") ? testCase.value : "normal";
+            testElements += createTestElement(familyName, testWeight, testStyle, testStretch,
+                                              testCase.value, expectedMatch, unexpectedMatch);
+        }
+    });
+}
+
+// Testcases (from web-platform/tests/css/css-fonts/variations/at-font-face-font-matching.html,
+// with a couple of extras). In each case, for the given property value, the testDescriptors
+// are listed from 'best' to 'worse' match, as evaluated by the font-matching algorithm in
+// https://drafts.csswg.org/css-fonts-4/#font-style-matching.
+testDescriptor("font-weight", [
+    { value: "400", testDescriptors: ["400", "450 460", "500", "350 399", "351 398", "501 550", "502 560"] },
+    { value: "430", testDescriptors: ["420 440", "450 460", "500", "400 425", "350 399", "340 398", "501 550", "502 560"] },
+    { value: "500", testDescriptors: ["500", "450 460", "400", "350 399", "351 398", "501 550", "502 560"] },
+    { value: "501", testDescriptors: ["501", "502 510", "503 520", "500", "450 460", "390 410", "300 350"] },
+    { value: "399", testDescriptors: ["350 399", "340 360", "200 300", "400", "450 460", "500 501", "502 510"] },
+    { value: "350", testDescriptors: ["200 300", "250 280", "420 450", "430 440", "445"] },
+    { value: "550", testDescriptors: ["600 800", "700 900", "420 450", "430 440", "425"] }
+]);
+
+testDescriptor("font-stretch", [
+    { value: "100%", testDescriptors: ["100%", "110% 120%", "115% 116%"] },
+    { value: "110%", testDescriptors: ["110% 120%", "115% 116%", "105%", "100%", "50% 80%", "60% 70%"] },
+    { value: "90%",  testDescriptors: ["90% 100%", "50% 80%", "60% 70%", "110% 140%", "120% 130%"] },
+]);
+
+testDescriptor("font-style", [
+    { value: "normal",         testDescriptors: ["normal", "oblique 0deg", "oblique 10deg 40deg", "oblique 20deg 30deg", "oblique -50deg -20deg", "oblique -40deg -30deg" ] },
+    { value: "italic",         testDescriptors: ["italic", "oblique 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 5deg 10deg", "oblique 5deg", "normal", "oblique 0deg", "oblique -60deg -30deg", "oblique -50deg -40deg" ] },
+    { value: "oblique 20deg",  testDescriptors: ["oblique 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 10deg", "italic", "oblique 0deg", "oblique -50deg -20deg", "oblique -40deg -30deg" ] },
+    { value: "oblique 21deg",  testDescriptors: ["oblique 21deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "oblique 20deg", "oblique 10deg", "italic", "oblique 0deg",  "oblique -50deg -20deg", "oblique -40deg -30deg" ] },
+    { value: "oblique 10deg",  testDescriptors: ["oblique 10deg", "oblique 5deg", "oblique 15deg 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "italic", "oblique 0deg", "oblique -50deg -20deg", "oblique -40deg -30deg" ] },
+    { value: "oblique 0deg",   testDescriptors: ["oblique 0deg", "oblique 5deg", "oblique 15deg 20deg", "oblique 30deg 60deg", "oblique 40deg 50deg", "italic", "oblique -50deg -20deg", "oblique -40deg -30deg" ] },
+    { value: "oblique -10deg", testDescriptors: ["oblique -10deg", "oblique -5deg", "oblique -1deg 0deg", "oblique -20deg -15deg", "oblique -60deg -30deg", "oblique -50deg -40deg", "italic", "oblique 0deg 10deg", "oblique 40deg 50deg" ] },
+    { value: "oblique -20deg", testDescriptors: ["oblique -20deg", "oblique -60deg -40deg", "oblique -10deg", "italic", "oblique 0deg", "oblique 30deg 60deg", "oblique 40deg 50deg"] },
+    { value: "oblique -21deg", testDescriptors: ["oblique -21deg", "oblique -60deg -40deg", "oblique -10deg", "italic", "oblique 0deg", "oblique 30deg 60deg", "oblique 40deg 50deg"] },
+]);
+
+// Stuff the @font-face rules and test elements into the document.
+// Any testcases that render Ahem glyphs are failures.
+document.getElementById("dynamicStyles").innerHTML = fontFaceRules;
+document.getElementById("testContents").innerHTML = testElements;
--- a/layout/reftests/font-matching/reftest.list
+++ b/layout/reftests/font-matching/reftest.list
@@ -153,10 +153,12 @@ skip-if(gtkWidget||/^Windows\x20NT\x206\
 # random-if(!OSX) != system-generic-fallback-zh-tw.html system-generic-fallback-zh-cn.html
 
 # Tests for legacy font family name (GDI-model families) matching;
 # these depend on specific fonts that are available as standard on macOS and Windows,
 # and are not expected to pass on platforms that don't have those same fonts.
 skip-if(!cocoaWidget) == legacy-family-names-1.html legacy-family-names-1-ref.html
 skip-if(!winWidget) == legacy-family-names-2.html legacy-family-names-2-ref.html
 
+== descriptor-ranges.html descriptor-ranges-ref.html
+
 # Reset default prefs.
 default-preferences
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1449243.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<div id="host"></div>
+<script>
+  host.attachShadow({ mode: 'open' }).innerHTML = `
+    <style>
+      div { color: green }
+    </style>
+    <div></div>
+  `;
+  document.body.offsetTop;
+  host.shadowRoot.styleSheets[0].insertRule("[foo] { color: green }", 0);
+  host.shadowRoot.querySelector("div").setAttribute("foo", "bar");
+</script>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -264,14 +264,15 @@ load 1413670.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1415353.html
 load 1418059.html
 test-pref(dom.animations-api.core.enabled,true) load 1418867.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
 load 1426312.html
 load 1439793.html
 load 1409183.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1445682.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1449243.html
 load 1450691.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1453206.html
 load 1454140.html
 load 1455108.html
 load 1457288.html
 load 1457985.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2491,17 +2491,17 @@ pref("security.dialog_enable_delay", 100
 pref("security.notification_enable_delay", 500);
 
 pref("security.csp.enable", true);
 pref("security.csp.experimentalEnabled", false);
 pref("security.csp.enableStrictDynamic", true);
 
 #if defined(DEBUG) && !defined(ANDROID)
 // about:welcome has been added until Bug 1448359 is fixed at which time home, newtab, and welcome will all be removed.
-pref("csp.content_privileged_about_uris_without_csp", "blank,home,newtab,printpreview,srcdoc,welcome");
+pref("csp.content_privileged_about_uris_without_csp", "blank,blocked,home,newtab,printpreview,srcdoc,welcome");
 #endif
 
 #ifdef NIGHTLY_BUILD
 pref("security.csp.enable_violation_events", true);
 #else
 pref("security.csp.enable_violation_events", false);
 #endif
 
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -22,17 +22,16 @@
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/TextUtils.h"
 #include <algorithm>
 #include "nsContentUtils.h"
 #include "prprf.h"
 #include "nsReadableUtils.h"
 #include "mozilla/net/MozURL_ffi.h"
 
-
 //
 // setenv MOZ_LOG nsStandardURL:5
 //
 static LazyLogModule gStandardURLLog("nsStandardURL");
 
 // The Chromium code defines its own LOG macro which we don't want
 #undef LOG
 #define LOG(args)     MOZ_LOG(gStandardURLLog, LogLevel::Debug, args)
@@ -3488,18 +3487,20 @@ FromIPCSegment(const nsACString& aSpec, 
         return true;
     }
 
     // A value of -1 means an empty segment, but < -1 is undefined.
     if (NS_WARN_IF(aSegment.length() < -1)) {
         return false;
     }
 
+    CheckedInt<uint32_t> segmentLen = aSegment.position();
+    segmentLen += aSegment.length();
     // Make sure the segment does not extend beyond the spec.
-    if (NS_WARN_IF(aSegment.position() + aSegment.length() > aSpec.Length())) {
+    if (NS_WARN_IF(!segmentLen.isValid() || segmentLen.value() > aSpec.Length())) {
         return false;
     }
 
     aTarget.mPos = aSegment.position();
     aTarget.mLen = aSegment.length();
 
     return true;
 }
--- a/netwerk/test/gtest/TestStandardURL.cpp
+++ b/netwerk/test/gtest/TestStandardURL.cpp
@@ -1,18 +1,20 @@
 #include "gtest/gtest.h"
 #include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH
 
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
 #include "nsIURL.h"
+#include "nsIStandardURL.h"
 #include "nsString.h"
 #include "nsPrintfCString.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIURIMutator.h"
+#include "mozilla/ipc/URIUtils.h"
 #include "mozilla/Unused.h"
 
 // In nsStandardURL.cpp
 extern nsresult Test_NormalizeIPv4(const nsACString& host, nsCString& result);
 
 
 TEST(TestStandardURL, Simple) {
     nsCOMPtr<nsIURI> url;
@@ -269,8 +271,21 @@ TEST(TestStandardURL, Mutator)
   nsCOMPtr<nsIURL> url;
   rv = NS_MutateURI(uri)
          .SetScheme(NS_LITERAL_CSTRING("https"))
          .Finalize(url);
   ASSERT_EQ(rv, NS_OK);
   ASSERT_EQ(url->GetSpec(out), NS_OK);
   ASSERT_TRUE(out == NS_LITERAL_CSTRING("https://mozilla.org/path?query#ref"));
 }
+
+TEST(TestStandardURL, Deserialize_Bug1392739)
+{
+  mozilla::ipc::StandardURLParams standard_params;
+  standard_params.urlType() = nsIStandardURL::URLTYPE_STANDARD;
+  standard_params.spec() = NS_LITERAL_CSTRING("");
+  standard_params.host() = mozilla::ipc::StandardURLSegment(4294967295, 1);
+
+  mozilla::ipc::URIParams params(standard_params);
+
+  nsCOMPtr<nsIURIMutator> mutator = do_CreateInstance(NS_STANDARDURLMUTATOR_CID);
+  ASSERT_EQ(mutator->Deserialize(params), NS_ERROR_FAILURE);
+}
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -176,17 +176,16 @@ impl<'lr> TShadowRoot for GeckoShadowRoo
 
         let author_styles = unsafe {
             &*(self.0.mServoStyles.mPtr as *const structs::RawServoAuthorStyles
                 as *const bindings::RawServoAuthorStyles)
         };
 
         let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
 
-        debug_assert!(!author_styles.stylesheets.dirty());
         debug_assert!(
             author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() ||
                 author_styles.stylesheets.is_empty()
         );
 
         &author_styles.data
     }
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -240851,16 +240851,21 @@
      {}
     ]
    ],
    "css/css-animations/animationevent-interface.js": [
     [
      {}
     ]
    ],
+   "css/css-animations/support/testcommon.js": [
+    [
+     {}
+    ]
+   ],
    "css/css-backgrounds/OWNERS": [
     [
      {}
     ]
    ],
    "css/css-backgrounds/background-attachment-local/aqua-yellow-32x32.png": [
     [
      {}
@@ -318154,16 +318159,118 @@
     ]
    ],
    "css/css-align/self-alignment/place-self-shorthand-006.html": [
     [
      "/css/css-align/self-alignment/place-self-shorthand-006.html",
      {}
     ]
    ],
+   "css/css-animations/CSSAnimation-animationName.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-animationName.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-canceling.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-canceling.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-effect.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-effect.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-finished.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-finished.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-getComputedTiming.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-getComputedTiming.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-getCurrentTime.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-getCurrentTime.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-id.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-id.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-pausing.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-pausing.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-playState.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-playState.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-ready.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-ready.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSAnimation-startTime.tentative.html": [
+    [
+     "/css/css-animations/CSSAnimation-startTime.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/CSSPseudoElement-getAnimations.tentative.html": [
+    [
+     "/css/css-animations/CSSPseudoElement-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/Document-getAnimations.tentative.html": [
+    [
+     "/css/css-animations/Document-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/Element-getAnimations-dynamic-changes.tentative.html": [
+    [
+     "/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/Element-getAnimations.tentative.html": [
+    [
+     "/css/css-animations/Element-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/KeyframeEffect-getKeyframes.tentative.html": [
+    [
+     "/css/css-animations/KeyframeEffect-getKeyframes.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/KeyframeEffect-target.tentative.html": [
+    [
+     "/css/css-animations/KeyframeEffect-target.tentative.html",
+     {}
+    ]
+   ],
    "css/css-animations/animation-iteration-count-calc.html": [
     [
      "/css/css-animations/animation-iteration-count-calc.html",
      {}
     ]
    ],
    "css/css-animations/animationevent-interface.html": [
     [
@@ -318180,16 +318287,28 @@
    "css/css-animations/animationevent-types.html": [
     [
      "/css/css-animations/animationevent-types.html",
      {
       "timeout": "long"
      }
     ]
    ],
+   "css/css-animations/event-dispatch.tentative.html": [
+    [
+     "/css/css-animations/event-dispatch.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-animations/event-order.tentative.html": [
+    [
+     "/css/css-animations/event-order.tentative.html",
+     {}
+    ]
+   ],
    "css/css-animations/pending-style-changes-001.html": [
     [
      "/css/css-animations/pending-style-changes-001.html",
      {}
     ]
    ],
    "css/css-backgrounds/background-331.html": [
     [
@@ -493599,16 +493718,84 @@
   "css/css-align/self-alignment/place-self-shorthand-006.html": [
    "d355eac038a7307e7a969632bc745ad25d8d6f43",
    "testharness"
   ],
   "css/css-align/ttwf-reftest-alignContent.html": [
    "c7b1d7a341e34e780c452e9e1ffcfdf0e2f15230",
    "visual"
   ],
+  "css/css-animations/CSSAnimation-animationName.tentative.html": [
+   "484e60296667832b9a8720363d3dc0e1954001c1",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-canceling.tentative.html": [
+   "c61db271a05da4ee43ea214256553fb870fb0aa9",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-effect.tentative.html": [
+   "0b97dbd81e5e23958bf88744b747b7fb52b4e62e",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-finished.tentative.html": [
+   "83d70e20fee6b8bc9bffd419daa95b38de95f583",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-getComputedTiming.tentative.html": [
+   "eea40904b1b4df30dd4b9b2cd6510edbdbb0ee09",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-getCurrentTime.tentative.html": [
+   "5bbff35e4e42648e95dee862834439594e45786b",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-id.tentative.html": [
+   "60eacd278dcd90d55608aeec11b46bdf55b77daa",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-pausing.tentative.html": [
+   "aecf0242a5cd1e84c807d711a5aba7aaa3996059",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-playState.tentative.html": [
+   "98bf2c7129030e5a04f9ad4e9c5b6867422fd8ca",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-ready.tentative.html": [
+   "c0b3148621c1ddf8002cf1c2cd9300690574979e",
+   "testharness"
+  ],
+  "css/css-animations/CSSAnimation-startTime.tentative.html": [
+   "fe1929b87adc6457c9635a24e579228189f84dce",
+   "testharness"
+  ],
+  "css/css-animations/CSSPseudoElement-getAnimations.tentative.html": [
+   "c5caf34d6486e2238ebcb6de2a4d0c4d1fdccefe",
+   "testharness"
+  ],
+  "css/css-animations/Document-getAnimations.tentative.html": [
+   "7604149916887b5b7bffdff886fa5de9d8691dd4",
+   "testharness"
+  ],
+  "css/css-animations/Element-getAnimations-dynamic-changes.tentative.html": [
+   "63bcea5d99380fea7d717207a65838c05226c2cf",
+   "testharness"
+  ],
+  "css/css-animations/Element-getAnimations.tentative.html": [
+   "e602c48a3ace0749321061c5f13798f2d64c0013",
+   "testharness"
+  ],
+  "css/css-animations/KeyframeEffect-getKeyframes.tentative.html": [
+   "e10b347a860fe423b0af02f92fda07ee05ec7abe",
+   "testharness"
+  ],
+  "css/css-animations/KeyframeEffect-target.tentative.html": [
+   "0620113244a7517a57f81e0047b7ef4b09b87d0f",
+   "testharness"
+  ],
   "css/css-animations/OWNERS": [
    "1f5f2cd90103ea32e65f81c7d36007d1ac156905",
    "support"
   ],
   "css/css-animations/animation-common-ref.html": [
    "ea85a554ca35bcfbbee1a59651f40045b8f916fb",
    "support"
   ],
@@ -493907,20 +494094,32 @@
   "css/css-animations/animationevent-types.html": [
    "15d495003fa9616fc54a4f6072e531341d51185f",
    "testharness"
   ],
   "css/css-animations/animationstart-and-animationend-events.html": [
    "d4692e2ac84a6dbbc9efd7937964d43ecd9dd109",
    "manual"
   ],
+  "css/css-animations/event-dispatch.tentative.html": [
+   "e5343fdccf63d69332fa1ad7a51460ac5284e4c8",
+   "testharness"
+  ],
+  "css/css-animations/event-order.tentative.html": [
+   "0115580619b629e47ae0f2635cc84e1e80442a8f",
+   "testharness"
+  ],
   "css/css-animations/pending-style-changes-001.html": [
    "5f2bf4b6712dd230109be62407cd31800451a271",
    "testharness"
   ],
+  "css/css-animations/support/testcommon.js": [
+   "3e2b733b29fca0963c95c0d069b7a518db266004",
+   "support"
+  ],
   "css/css-backgrounds/OWNERS": [
    "656d9f4e3a66f8cb955910171b9997140e4bbd8e",
    "support"
   ],
   "css/css-backgrounds/background-331.html": [
    "28185e9f9710a676579fa8de6cc39e1febc9e16f",
    "testharness"
   ],
@@ -615612,37 +615811,37 @@
    "b8cc580e3e8d17961ffff4b693857f6c333dd57f",
    "testharness"
   ],
   "web-animations/timing-model/animation-effects/simple-iteration-progress.html": [
    "602fe7e6880e0b18329262699872c696f451d744",
    "testharness"
   ],
   "web-animations/timing-model/animations/canceling-an-animation.html": [
-   "e03baa30d438529a0ebe39f0f623563aa9850d74",
+   "c2750f33d773b01a9ed5ac4bb8c9f65f7e78265a",
    "testharness"
   ],
   "web-animations/timing-model/animations/finishing-an-animation.html": [
    "afe654435332e798b3771b6ec6ca13bcca99e421",
    "testharness"
   ],
   "web-animations/timing-model/animations/pausing-an-animation.html": [
-   "a4cb7b89c778ad5c294eeb55e94461e19ca8eb4b",
+   "55acd5e4394d3c2543a00d90656a8134c497287b",
    "testharness"
   ],
   "web-animations/timing-model/animations/play-states.html": [
    "0ab2fa3a464001272d1af541ea769fa967490c3b",
    "testharness"
   ],
   "web-animations/timing-model/animations/playing-an-animation.html": [
-   "10580a1e72892208a14c6fe55091e998edf0171c",
+   "1ef3259d06115d748ee92c143bdf07d9ce665224",
    "testharness"
   ],
   "web-animations/timing-model/animations/reversing-an-animation.html": [
-   "72b89e78ca7dac261af8de370389d89c810b3718",
+   "053cc1287bd6dfc9688b4e1700a15bedc11bd92b",
    "testharness"
   ],
   "web-animations/timing-model/animations/seamlessly-updating-the-playback-rate-of-an-animation.html": [
    "a7e28aa0b40a39b00da257e347cb6ecf8d1d2882",
    "testharness"
   ],
   "web-animations/timing-model/animations/setting-the-current-time-of-an-animation.html": [
    "aa5f258132490ade2dbd9485c85f749cbab293a5",
--- a/testing/web-platform/tests/content-security-policy/generic/policy-inherited-correctly-by-plznavigate.html
+++ b/testing/web-platform/tests/content-security-policy/generic/policy-inherited-correctly-by-plznavigate.html
@@ -17,23 +17,25 @@
   <iframe id="x" srcdoc="<a href='about:blank'>123</a>"></iframe>
 
   <script>
     window.onmessage = t.step_func_done(function(e) {
       assert_equals(e.data, "frame-src");
     });
 
     x = document.getElementById('x');
-    x.location = "";
+    x.onload = function() {
+      x.location = "";
 
-    // While document.write is deprecated I did not find another way to reproduce
-    // the original exploit.
-    x.contentDocument.write(
-      '<script>window.addEventListener("securitypolicyviolation", function(e) {' +
-      '  window.top.postMessage(e.violatedDirective, "*");' +
-      '});</scr' + 'ipt>' +
-      '<iframe src="../support/fail.html"></iframe>'
-    );
-    x.contentDocument.close();
+      // While document.write is deprecated I did not find another way to reproduce
+      // the original exploit.
+      x.contentDocument.write(
+        '<script>window.addEventListener("securitypolicyviolation", function(e) {' +
+        '  window.top.postMessage(e.violatedDirective, "*");' +
+        '});</scr' + 'ipt>' +
+        '<iframe src="../support/fail.html"></iframe>'
+      );
+      x.contentDocument.close();
+    }
   </script>
   <script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-src%20%27none%27''></script>
 </body>
-</html>
\ No newline at end of file
+</html>
rename from dom/animation/test/css-animations/test_cssanimation-animationname.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html
--- a/dom/animation/test/css-animations/test_cssanimation-animationname.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html
@@ -1,39 +1,42 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.animationName</title>
+<link rel="help"
+      href="https://drafts.csswg.org/css-animations-2/#dom-cssanimation-animationname">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes xyz {
   to { left: 100px }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'xyz 100s';
   assert_equals(div.getAnimations()[0].animationName, 'xyz',
                 'Animation name matches keyframes rule name');
 }, 'Animation name makes keyframe rule');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'x\\yz 100s';
   assert_equals(div.getAnimations()[0].animationName, 'xyz',
                 'Escaped animation name matches keyframes rule name');
 }, 'Escaped animation name');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'x\\79 z 100s';
   assert_equals(div.getAnimations()[0].animationName, 'xyz',
                 'Hex-escaped animation name matches keyframes rule'
                 + ' name');
 }, 'Animation name with hex-escape');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-cancel.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html
--- a/dom/animation/test/css-animations/test_animation-cancel.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html
@@ -1,13 +1,16 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Canceling a CSS animation</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes translateAnim {
   to { transform: translate(100px) }
 }
 @keyframes marginLeftAnim {
   to { margin-left: 100px }
 }
 @keyframes marginLeftAnim100To200 {
@@ -15,102 +18,103 @@
   to { margin-left: 200px }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: translateAnim 100s' });
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: translateAnim 100s' });
+  const animation = div.getAnimations()[0];
+
+  await animation.ready;
 
-  return animation.ready.then(function() {
-    assert_not_equals(getComputedStyle(div).transform, 'none',
-                      'transform style is animated before cancelling');
-    animation.cancel();
-    assert_equals(getComputedStyle(div).transform, 'none',
-                  'transform style is no longer animated after cancelling');
-  });
-}, 'Animated style is cleared after cancelling a running CSS animation');
+  assert_not_equals(getComputedStyle(div).transform, 'none',
+                    'transform style is animated before canceling');
+  animation.cancel();
+  assert_equals(getComputedStyle(div).transform, 'none',
+                'transform style is no longer animated after canceling');
+}, 'Animated style is cleared after canceling a running CSS animation');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: translateAnim 100s forwards' });
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: translateAnim 100s forwards' });
+  const animation = div.getAnimations()[0];
   animation.finish();
 
-  return animation.ready.then(function() {
-    assert_not_equals(getComputedStyle(div).transform, 'none',
-                      'transform style is filling before cancelling');
-    animation.cancel();
-    assert_equals(getComputedStyle(div).transform, 'none',
-                  'fill style is cleared after cancelling');
-  });
-}, 'Animated style is cleared after cancelling a filling CSS animation');
+  await animation.ready;
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' });
-  var animation = div.getAnimations()[0];
+  assert_not_equals(getComputedStyle(div).transform, 'none',
+                    'transform style is filling before canceling');
+  animation.cancel();
+  assert_equals(getComputedStyle(div).transform, 'none',
+                'fill style is cleared after canceling');
+}, 'Animated style is cleared after canceling a filling CSS animation');
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' });
+  const animation = div.getAnimations()[0];
   animation.cancel();
 
   assert_equals(getComputedStyle(div).marginLeft, '0px',
-                'margin-left style is not animated after cancelling');
+                'margin-left style is not animated after canceling');
 
   animation.currentTime = 50 * 1000;
   assert_equals(getComputedStyle(div).marginLeft, '50px',
-                'margin-left style is updated when cancelled animation is'
+                'margin-left style is updated when canceled animation is'
                 + ' seeked');
 }, 'After canceling an animation, it can still be seeked');
 
-promise_test(function(t) {
-  var div =
+promise_test(async t => {
+  const div =
     addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
 
-  return animation.ready.then(function() {
-    animation.cancel();
-    assert_equals(getComputedStyle(div).marginLeft, '0px',
-                  'margin-left style is not animated after cancelling');
-    animation.play();
-    assert_equals(getComputedStyle(div).marginLeft, '100px',
-                  'margin-left style is animated after re-starting animation');
-    return animation.ready;
-  }).then(function() {
-    assert_equals(animation.playState, 'running',
-                  'Animation succeeds in running after being re-started');
-  });
-}, 'After cancelling an animation, it can still be re-used');
+  await animation.ready;
 
-test(function(t) {
-  var div =
-    addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
-  var animation = div.getAnimations()[0];
   animation.cancel();
   assert_equals(getComputedStyle(div).marginLeft, '0px',
-                'margin-left style is not animated after cancelling');
+                'margin-left style is not animated after canceling');
+  animation.play();
+  assert_equals(getComputedStyle(div).marginLeft, '100px',
+                'margin-left style is animated after re-starting animation');
+
+  await animation.ready;
+
+  assert_equals(animation.playState, 'running',
+                'Animation succeeds in running after being re-started');
+}, 'After canceling an animation, it can still be re-used');
+
+test(t => {
+  const div =
+    addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
+  const animation = div.getAnimations()[0];
+  animation.cancel();
+  assert_equals(getComputedStyle(div).marginLeft, '0px',
+                'margin-left style is not animated after canceling');
 
   // Trigger a change to some animation properties and check that this
   // doesn't cause the animation to become live again
   div.style.animationDuration = '200s';
   assert_equals(getComputedStyle(div).marginLeft, '0px',
                 'margin-left style is still not animated after updating'
                 + ' animation-duration');
   assert_equals(animation.playState, 'idle',
                 'Animation is still idle after updating animation-duration');
-}, 'After cancelling an animation, updating animation properties doesn\'t make'
+}, 'After canceling an animation, updating animation properties doesn\'t make'
    + ' it live again');
 
-test(function(t) {
-  var div =
+test(t => {
+  const div =
     addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   animation.cancel();
   assert_equals(getComputedStyle(div).marginLeft, '0px',
-                'margin-left style is not animated after cancelling');
+                'margin-left style is not animated after canceling');
 
   // Make some changes to animation-play-state and check that the
   // animation doesn't become live again. This is because it should be
   // possible to cancel an animation from script such that all future
   // changes to style are ignored.
 
   // Redundant change
   div.style.animationPlayState = 'running';
@@ -125,69 +129,71 @@ test(function(t) {
                 + ' animation-play-state: paused');
 
   // Play
   div.style.animationPlayState = 'running';
   assert_equals(animation.playState, 'idle',
                 'Animation is still idle after re-setting'
                 + ' animation-play-state: running');
 
-}, 'After cancelling an animation, updating animation-play-state doesn\'t'
+}, 'After canceling an animation, updating animation-play-state doesn\'t'
    + ' make it live again');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: translateAnim 10s both' });
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: translateAnim 10s both' });
   div.style.marginLeft = '0px';
 
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
 
-  return animation.ready.then(function() {
-    assert_equals(animation.playState, 'running');
+  await animation.ready;
+
+  assert_equals(animation.playState, 'running');
 
-    div.style.animationName = 'none';
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(animation.playState, 'idle');
-    assert_equals(getComputedStyle(div).marginLeft, '0px');
-  });
+  div.style.animationName = 'none';
+  flushComputedStyle(div);
+
+  await waitForFrame();
+
+  assert_equals(animation.playState, 'idle');
+  assert_equals(getComputedStyle(div).marginLeft, '0px');
 }, 'Setting animation-name to \'none\' cancels the animation');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: translateAnim 10s both' });
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: translateAnim 10s both' });
+  const animation = div.getAnimations()[0];
 
-  return animation.ready.then(function() {
-    assert_equals(animation.playState, 'running');
+  await animation.ready;
 
-    div.style.display = 'none';
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(animation.playState, 'idle');
-    assert_equals(getComputedStyle(div).marginLeft, '0px');
-  });
+  assert_equals(animation.playState, 'running');
+
+  div.style.display = 'none';
+
+  await waitForFrame();
+
+  assert_equals(animation.playState, 'idle');
+  assert_equals(getComputedStyle(div).marginLeft, '0px');
 }, 'Setting display:none on an element cancel its animations');
 
-promise_test(function(t) {
-  var parentDiv = addDiv(t);
-  var childDiv  = document.createElement('div');
+promise_test(async t => {
+  const parentDiv = addDiv(t);
+  const childDiv  = document.createElement('div');
   parentDiv.appendChild(childDiv);
 
   childDiv.setAttribute('style', 'animation: translateAnim 10s both');
   flushComputedStyle(childDiv);
 
-  var animation = childDiv.getAnimations()[0];
+  const animation = childDiv.getAnimations()[0];
 
-  return animation.ready.then(function() {
-    assert_equals(animation.playState, 'running');
+  await animation.ready;
+
+  assert_equals(animation.playState, 'running');
 
-    parentDiv.style.display = 'none';
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(animation.playState, 'idle');
-    assert_equals(getComputedStyle(childDiv).marginLeft, '0px');
-  });
+  parentDiv.style.display = 'none';
+  await waitForFrame();
+
+  assert_equals(animation.playState, 'idle');
+  assert_equals(getComputedStyle(childDiv).marginLeft, '0px');
 }, 'Setting display:none on an ancestor element cancels animations on ' +
    'descendants');
 
 </script>
 </body>
 </html>
rename from dom/animation/test/css-animations/test_setting-effect.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html
--- a/dom/animation/test/css-animations/test_setting-effect.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html
@@ -1,132 +1,132 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.effect</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src='../testcommon.js'></script>
+<script src="support/testcommon.js"></script>
 <style>
-  @keyframes anim {
-    from {
-      margin-left: 0px;
-    }
-    to {
-      margin-left: 100px;
-    }
+@keyframes anim {
+  from {
+    margin-left: 0px;
   }
+  to {
+    margin-left: 100px;
+  }
+}
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim 100s';
 
-  var watcher = new EventWatcher(t, div, [ 'animationend',
-                                           'animationcancel' ]);
-  var animation = div.getAnimations()[0];
-  return animation.ready.then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    animation.effect = null;
-    assert_equals(animation.playState, 'finished');
-    assert_equals(getComputedStyle(div).marginLeft, '0px');
-    return watcher.wait_for('animationend');
-  });
+  const watcher = new EventWatcher(t, div, [ 'animationend',
+                                             'animationcancel' ]);
+  const animation = div.getAnimations()[0];
+  await animation.ready;
+
+  animation.currentTime = 50 * MS_PER_SEC;
+  animation.effect = null;
+  assert_equals(animation.playState, 'finished');
+  assert_equals(getComputedStyle(div).marginLeft, '0px');
+  await watcher.wait_for('animationend');
 }, 'Setting a null effect on a running animation fires an animationend event');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim 100s';
 
-  var animation = div.getAnimations()[0];
-  return animation.ready.then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    animation.effect = new KeyframeEffect(div,
-                                          { left: [ '0px' , '100px'] },
-                                          100 * MS_PER_SEC);
-    assert_equals(animation.playState, 'running');
-    assert_equals(getComputedStyle(div).marginLeft, '0px');
-    assert_equals(getComputedStyle(div).left, '50px');
-  });
+  const animation = div.getAnimations()[0];
+  await animation.ready;
+
+  animation.currentTime = 50 * MS_PER_SEC;
+  animation.effect = new KeyframeEffect(div,
+                                        { left: [ '0px' , '100px'] },
+                                        100 * MS_PER_SEC);
+  assert_equals(getComputedStyle(div).marginLeft, '0px');
+  assert_equals(getComputedStyle(div).left, '50px');
 }, 'Replacing an animation\'s effect with an effect that targets a different ' +
    'property should update both properties');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim 100s';
 
-  var animation = div.getAnimations()[0];
-  return animation.ready.then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    animation.effect = new KeyframeEffect(div,
-                                          { left: [ '0px' , '100px'] },
-                                          20 * MS_PER_SEC);
-    assert_equals(animation.playState, 'finished');
-  });
+  const animation = div.getAnimations()[0];
+  await animation.ready;
+
+  animation.currentTime = 50 * MS_PER_SEC;
+  animation.effect = new KeyframeEffect(div,
+                                        { left: [ '0px' , '100px'] },
+                                        20 * MS_PER_SEC);
+  assert_equals(animation.playState, 'finished');
 }, 'Replacing an animation\'s effect with a shorter one that should have ' +
    'already finished, the animation finishes immediately');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim 100s';
 
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_true(animation.pending);
 
   animation.effect = new KeyframeEffect(div,
                                         { left: [ '0px' , '100px'] },
                                         100 * MS_PER_SEC);
   assert_true(animation.pending);
 
-  return animation.ready.then(function() {
-    assert_false(animation.pending);
-  });
+  await animation.ready;
+
+  assert_false(animation.pending);
 }, 'A play-pending animation\'s effect whose effect is replaced still exits ' +
    'the pending state');
 
-promise_test(function(t) {
-  var div1 = addDiv(t);
-  var div2 = addDiv(t);
+promise_test(async t => {
+  const div1 = addDiv(t);
+  const div2 = addDiv(t);
 
-  var watcher1 = new EventWatcher(t, div1, 'animationstart');
+  const watcher1 = new EventWatcher(t, div1, 'animationstart');
   // Watch |div2| as well to ensure it does *not* get events.
-  var watcher2 = new EventWatcher(t, div2, 'animationstart');
+  const watcher2 = new EventWatcher(t, div2, 'animationstart');
 
   div1.style.animation = 'anim 100s';
 
-  var animation = div1.getAnimations()[0];
+  const animation = div1.getAnimations()[0];
   animation.effect = new KeyframeEffect(div2,
                                         { left: [ '0px', '100px' ] },
                                         100 * MS_PER_SEC);
 
-  return watcher1.wait_for('animationstart').then(function() {
-    assert_equals(animation.effect.target, div2);
+  await watcher1.wait_for('animationstart');
+
+  assert_equals(animation.effect.target, div2);
 
-    // Then wait a couple of frames and check that no event was dispatched.
-    return waitForAnimationFrames(2);
-  });
-}, 'The event is dispatched at the original element even after setting an ' +
-   'effect with a different target element');
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'CSS animation events are dispatched at the original element even after'
+   + ' setting an effect with a different target element');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var watcher = new EventWatcher(t, div, [ 'animationstart',
-                                           'animationend',
-                                           'animationcancel' ]);
+promise_test(async t => {
+  const div = addDiv(t);
+  const watcher = new EventWatcher(t, div, [ 'animationstart',
+                                             'animationend',
+                                             'animationcancel' ]);
   div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   animation.finish();
 
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(function(evt) {
-    // Set a longer effect
-    animation.effect = new KeyframeEffect(div,
-                                          { left: [ '0px', '100px' ] },
-                                          200 * MS_PER_SEC);
-    return watcher.wait_for('animationstart');
-  });
+  await watcher.wait_for([ 'animationstart', 'animationend' ]);
+  // Set a longer effect
+  animation.effect = new KeyframeEffect(div,
+                                        { left: [ '0px', '100px' ] },
+                                        200 * MS_PER_SEC);
+  await watcher.wait_for('animationstart');
 }, 'After replacing a finished animation\'s effect with a longer one ' +
    'it fires an animationstart event');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-finished.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html
--- a/dom/animation/test/css-animations/test_animation-finished.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html
@@ -1,95 +1,87 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.finished</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes abc {
   to { transform: translate(10px) }
 }
 @keyframes def {}
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
 const ANIM_PROP_VAL = 'abc 100s';
 const ANIM_DURATION = 100 * MS_PER_SEC;
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
+
   // Set up pending animation
   div.style.animation = ANIM_PROP_VAL;
-  var animation = div.getAnimations()[0];
-  var previousFinishedPromise = animation.finished;
-  // Set up listeners on finished promise
-  var retPromise = animation.finished.then(function() {
-    assert_unreached('finished promise is fulfilled');
-  }).catch(function(err) {
-    assert_equals(err.name, 'AbortError',
-                  'finished promise is rejected with AbortError');
-    assert_not_equals(animation.finished, previousFinishedPromise,
-                      'Finished promise should change after the original is ' +
-                      'rejected');
-  });
+  const animation = div.getAnimations()[0];
+  const originalFinishedPromise = animation.finished;
 
-  // Now cancel the animation and flush styles
+  // Cancel the animation and flush styles
   div.style.animation = '';
   getComputedStyle(div).animation;
 
-  return retPromise;
-}, 'finished promise is rejected when an animation is cancelled by resetting ' +
+  await promise_rejects(t, 'AbortError', originalFinishedPromise,
+                        'finished promise is rejected with AbortError');
+
+  assert_not_equals(animation.finished, originalFinishedPromise,
+                    'Finished promise should change after the original is ' +
+                    'rejected');
+}, 'finished promise is rejected when an animation is canceled by resetting ' +
    'the animation property');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   // As before, but this time instead of removing all animations, simply update
   // the list of animations. At least for Firefox, updating is a different
   // code path.
 
   // Set up pending animation
   div.style.animation = ANIM_PROP_VAL;
-  var animation = div.getAnimations()[0];
-  var previousFinishedPromise = animation.finished;
+  const animation = div.getAnimations()[0];
+  const originalFinishedPromise = animation.finished;
 
-  // Set up listeners on finished promise
-  var retPromise = animation.finished.then(function() {
-    assert_unreached('finished promise was fulfilled');
-  }).catch(function(err) {
-    assert_equals(err.name, 'AbortError',
-                  'finished promise is rejected with AbortError');
-    assert_not_equals(animation.finished, previousFinishedPromise,
-                      'Finished promise should change after the original is ' +
-                      'rejected');
-  });
-
-  // Now update the animation and flush styles
+  // Update the animation and flush styles
   div.style.animation = 'def 100s';
   getComputedStyle(div).animation;
 
-  return retPromise;
-}, 'finished promise is rejected when an animation is cancelled by changing ' +
+  await promise_rejects(t, 'AbortError', originalFinishedPromise,
+                        'finished promise is rejected with AbortError');
+
+  assert_not_equals(animation.finished, originalFinishedPromise,
+                    'Finished promise should change after the original is ' +
+                    'rejected');
+}, 'finished promise is rejected when an animation is canceled by changing ' +
    'the animation property');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = ANIM_PROP_VAL;
-  var animation = div.getAnimations()[0];
-  var previousFinishedPromise = animation.finished;
+  const animation = div.getAnimations()[0];
+  const originalFinishedPromise = animation.finished;
   animation.currentTime = ANIM_DURATION;
-  return animation.finished.then(function() {
-    div.style.animationPlayState = 'running';
-    return waitForAnimationFrames(2);
-  }).then(function() {
-    assert_equals(animation.finished, previousFinishedPromise,
-                  'Should not replay when animation-play-state changes to ' +
-                  '"running" on finished animation');
-    assert_equals(animation.currentTime, ANIM_DURATION,
-                  'currentTime should not change when animation-play-state ' +
-                  'changes to "running" on finished animation');
-  });
-}, 'Test finished promise changes when animationPlayState set to running');
+
+  await animation.finished;
+
+  div.style.animationPlayState = 'running';
+  await waitForAnimationFrames(2);
+
+  assert_equals(animation.finished, originalFinishedPromise,
+                'The finished promise should NOT be reset');
+  assert_equals(animation.currentTime, ANIM_DURATION,
+                'Sanity check: the current time should not change');
+}, 'finished promise is not reset when animationPlayState is set to running');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-computed-timing.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-getComputedTiming.tentative.html
--- a/dom/animation/test/css-animations/test_animation-computed-timing.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-getComputedTiming.tentative.html
@@ -1,71 +1,76 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.getComputedTiming()</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes moveAnimation {
   from { margin-left: 100px }
   to { margin-left: 200px }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 
 'use strict';
 
 // --------------------
 // delay
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
-  assert_equals(effect.getComputedTiming().delay, 0,
-                'Initial value of delay');
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 0, 'Initial value of delay');
 }, 'delay of a new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s -10s'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s -10s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().delay, -10 * MS_PER_SEC,
                 'Initial value of delay');
 }, 'Negative delay of a new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 10s'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 10s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().delay, 10 * MS_PER_SEC,
                 'Initial value of delay');
 }, 'Positive delay of a new animation');
 
 
 // --------------------
 // endDelay
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endDelay, 0,
                 'Initial value of endDelay');
 }, 'endDelay of a new animation');
 
 
 // --------------------
 // fill
 // --------------------
-test(function(t) {
-  var getEffectWithFill = function(fill) {
-    var div = addDiv(t, {style: 'animation: moveAnimation 100s ' + fill});
+
+test(t => {
+  const getEffectWithFill = fill => {
+    const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + fill });
     return div.getAnimations()[0].effect;
   };
 
-  var effect = getEffectWithFill('');
+  let effect = getEffectWithFill('');
   assert_equals(effect.getComputedTiming().fill, 'none',
                 'Initial value of fill');
   effect = getEffectWithFill('forwards');
   assert_equals(effect.getComputedTiming().fill, 'forwards',
                 'Fill forwards');
   effect = getEffectWithFill('backwards');
   assert_equals(effect.getComputedTiming().fill, 'backwards',
                 'Fill backwards');
@@ -73,70 +78,76 @@ test(function(t) {
   assert_equals(effect.getComputedTiming().fill, 'both',
                 'Fill forwards and backwards');
 }, 'fill of a new animation');
 
 
 // --------------------
 // iterationStart
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterationStart, 0,
                 'Initial value of iterationStart');
 }, 'iterationStart of a new animation');
 
 
 // --------------------
 // iterations
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterations, 1,
                 'Initial value of iterations');
 }, 'iterations of a new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2016.5'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 2016.5' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterations, 2016.5,
                 'Initial value of iterations');
 }, 'iterations of a finitely repeating animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s infinite'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterations, Infinity,
                 'Initial value of iterations');
 }, 'iterations of an infinitely repeating animation');
 
 
 // --------------------
 // duration
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s -10s infinite'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 100s -10s infinite'
+  });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().duration, 100 * MS_PER_SEC,
                 'Initial value of duration');
 }, 'duration of a new animation');
 
 
 // --------------------
 // direction
 // --------------------
-test(function(t) {
-  var getEffectWithDir = function(dir) {
-    var div = addDiv(t, {style: 'animation: moveAnimation 100s ' + dir});
+
+test(t => {
+  const getEffectWithDir = dir => {
+    const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + dir });
     return div.getAnimations()[0].effect;
   };
 
-  var effect = getEffectWithDir('');
+  let effect = getEffectWithDir('');
   assert_equals(effect.getComputedTiming().direction, 'normal',
                 'Initial value of normal direction');
   effect = getEffectWithDir('reverse');
   assert_equals(effect.getComputedTiming().direction, 'reverse',
                 'Initial value of reverse direction');
   effect = getEffectWithDir('alternate');
   assert_equals(effect.getComputedTiming().direction, 'alternate',
                 'Initial value of alternate direction');
@@ -144,173 +155,191 @@ test(function(t) {
   assert_equals(effect.getComputedTiming().direction, 'alternate-reverse',
                 'Initial value of alternate-reverse direction');
 }, 'direction of a new animation');
 
 
 // --------------------
 // easing
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().easing, 'linear',
                 'Initial value of easing');
 }, 'easing of a new animation');
 
 
 // ------------------------------
 // endTime
 // = max(start delay + active duration + end delay, 0)
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC,
                 'Initial value of endTime');
 }, 'endTime of an new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s -5s'});
-  var effect = div.getAnimations()[0].effect;
-  var answer = (100 - 5) * MS_PER_SEC;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s -5s' });
+  const effect = div.getAnimations()[0].effect;
+  const answer = (100 - 5) * MS_PER_SEC;
   assert_equals(effect.getComputedTiming().endTime, answer,
                 'Initial value of endTime');
 }, 'endTime of an animation with a negative delay');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 10s -100s infinite'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 10s -100s infinite'
+  });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endTime, Infinity,
                 'Initial value of endTime');
 }, 'endTime of an infinitely repeating animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 100s infinite'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s 100s infinite' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC,
                 'Initial value of endTime');
 }, 'endTime of an infinitely repeating zero-duration animation');
 
-test(function(t) {
+test(t => {
   // Fill forwards so div.getAnimations()[0] won't return an
   // undefined value.
-  var div = addDiv(t, {style: 'animation: moveAnimation 10s -100s forwards'});
-  var effect = div.getAnimations()[0].effect;
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 10s -100s forwards'
+  });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endTime, 0,
                 'Initial value of endTime');
 }, 'endTime of an animation that finishes before its startTime');
 
 
 // --------------------
 // activeDuration
 // = iteration duration * iteration count
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 5'});
-  var effect = div.getAnimations()[0].effect;
-  var answer = 100 * MS_PER_SEC * 5;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 5' });
+  const effect = div.getAnimations()[0].effect;
+  const answer = 100 * MS_PER_SEC * 5;
   assert_equals(effect.getComputedTiming().activeDuration, answer,
                 'Initial value of activeDuration');
 }, 'activeDuration of a new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s infinite'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().activeDuration, Infinity,
                 'Initial value of activeDuration');
 }, 'activeDuration of an infinitely repeating animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 1s infinite'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s 1s infinite' });
+  const effect = div.getAnimations()[0].effect;
   // If either the iteration duration or iteration count are zero,
   // the active duration is zero.
   assert_equals(effect.getComputedTiming().activeDuration, 0,
                 'Initial value of activeDuration');
 }, 'activeDuration of an infinitely repeating zero-duration animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 1s 0'});
-  var effect = div.getAnimations()[0].effect;
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 1s 0' });
+  const effect = div.getAnimations()[0].effect;
   // If either the iteration duration or iteration count are zero,
   // the active duration is zero.
   assert_equals(effect.getComputedTiming().activeDuration, 0,
                 'Initial value of activeDuration');
 }, 'activeDuration of an animation with zero iterations');
 
 
 // --------------------
 // localTime
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().localTime, 0,
                 'Initial value of localTime');
 }, 'localTime of a new animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const anim = div.getAnimations()[0];
   anim.currentTime = 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
                 'current localTime after setting currentTime');
 }, 'localTime of an animation is always equal to currentTime');
 
-promise_test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
 
-  var anim = div.getAnimations()[0];
+  const anim = div.getAnimations()[0];
   anim.playbackRate = 2; // 2 times faster
 
-  return anim.ready.then(function() {
-    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
-                  'localTime is equal to currentTime');
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
-                  'localTime is equal to currentTime');
-  });
+  await anim.ready;
+
+  assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                'localTime is equal to currentTime');
+
+  await waitForFrame();
+
+  assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                'localTime is equal to currentTime');
 }, 'localTime reflects playbackRate immediately');
 
-test(function(t) {
-  var div = addDiv(t);
-  var effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
+test(t => {
+  const div = addDiv(t);
+  const effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
 
   assert_equals(effect.getComputedTiming().localTime, null,
                 'localTime for orphaned effect');
 }, 'localTime of an AnimationEffect without an Animation');
 
 
 // --------------------
 // progress
-// Note: Default timing function is linear.
+//
+// Note: Even though CSS animations have a default animation-timing-function of
+// "ease", this only applies between keyframes (often referred to as the
+// keyframe-level easing). The progress value returned by getComputedTiming(),
+// however, only reflects effect-level easing and this defaults to "linear",
+// even for CSS animations.
 // --------------------
-test(function(t) {
-  [{fill: '',          progress: [ null, null ]},
-   {fill: 'none',      progress: [ null, null ]},
-   {fill: 'forwards',  progress: [ null, 1.0 ]},
-   {fill: 'backwards', progress: [ 0.0, null ]},
-   {fill: 'both',      progress: [ 0.0, 1.0 ]}]
-  .forEach(function(test) {
-    var div =
-      addDiv(t, {style: 'animation: moveAnimation 100s 10s ' + test.fill});
-    var anim = div.getAnimations()[0];
+
+test(t => {
+  const tests = [
+    { fill: '', progress: [null, null] },
+    { fill: 'none', progress: [null, null] },
+    { fill: 'forwards', progress: [null, 1.0] },
+    { fill: 'backwards', progress: [0.0, null] },
+    { fill: 'both', progress: [0.0, 1.0] },
+  ];
+  for (const test of tests) {
+    const div = addDiv(t, {
+      style: 'animation: moveAnimation 100s 10s ' + test.fill
+    });
+    const anim = div.getAnimations()[0];
     assert_true(anim.effect.getComputedTiming().progress === test.progress[0],
-                'initial progress with "' + test.fill + '" fill');
+                `Initial progress with "${test.fill}" fill`);
     anim.finish();
     assert_true(anim.effect.getComputedTiming().progress === test.progress[1],
-                'finished progress with "' + test.fill + '" fill');
-  });
+                `Initial progress with "${test.fill}" fill`);
+  }
 }, 'progress of an animation with different fill modes');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10 both'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 10s 10 both' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Initial value of progress');
   anim.currentTime += 2.5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
   anim.currentTime += 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
@@ -318,80 +347,83 @@ test(function(t) {
   anim.currentTime += 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
   anim.finish()
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
                 'Value of progress');
 }, 'progress of an integral repeating animation with normal direction');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
   // Note: FillMode here is "both" because
   // 1. Since this a zero-duration animation, it will already have finished
   //    so it won't be returned by getAnimations() unless it fills forwards.
   // 2. Fill backwards, so the progress before phase wouldn't be
   //    unresolved (null value).
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s infinite both'});
-  var anim = div.getAnimations()[0];
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
                 'Initial value of progress in after phase');
 
   // Seek backwards
   anim.currentTime -= 1 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Value of progress before phase');
 }, 'progress of an infinitely repeating zero-duration animation');
 
-test(function(t) {
+test(t => {
   // Default iterations = 1
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s both'});
-  var anim = div.getAnimations()[0];
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s both' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
                 'Initial value of progress in after phase');
 
   // Seek backwards
   anim.currentTime -= 1 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Value of progress before phase');
 }, 'progress of a finitely repeating zero-duration animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 5s 10.25 both'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s 5s 10.25 both' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Initial value of progress (before phase)');
 
   // Using iteration duration of 1 now.
   // currentIteration now is floor(10.25) = 10, so progress should be 25%.
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress in after phase');
 }, 'progress of a non-integral repeating zero-duration animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 5s 10.25 both reverse'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 0s 5s 10.25 both reverse',
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
                 'Initial value of progress (before phase)');
 
   // Seek forwards
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Value of progress in after phase');
 }, 'Progress of a non-integral repeating zero-duration animation ' +
    'with reversing direction');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10.25 both alternate'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 10s 10.25 both alternate',
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Initial value of progress');
   anim.currentTime += 2.5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
   anim.currentTime += 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
@@ -400,19 +432,21 @@ test(function(t) {
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Value of progress');
   anim.finish()
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
 }, 'progress of a non-integral repeating animation ' +
    'with alternate direction');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10.25 both alternate-reverse'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 10s 10.25 both alternate-reverse',
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
                 'Initial value of progress');
   anim.currentTime += 2.5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Value of progress');
   anim.currentTime += 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
@@ -421,37 +455,41 @@ test(function(t) {
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
   anim.finish()
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Value of progress');
 }, 'progress of a non-integral repeating animation ' +
    'with alternate-reversing direction');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.25 both alternate'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 0s 10.25 both alternate',
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Initial value of progress');
   anim.currentTime += 2.5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
   anim.currentTime -= 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.0,
                 'Value of progress');
   anim.finish()
   assert_equals(anim.effect.getComputedTiming().progress, 0.25,
                 'Value of progress');
 }, 'progress of a non-integral repeating zero-duration animation ' +
    'with alternate direction');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.25 both alternate-reverse'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 0s 10.25 both alternate-reverse',
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Initial value of progress');
   anim.currentTime += 2.5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 0.75,
                 'Value of progress');
   anim.currentTime -= 5 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().progress, 1.0,
@@ -461,106 +499,109 @@ test(function(t) {
                 'Value of progress');
 }, 'progress of a non-integral repeating zero-duration animation ' +
    'with alternate-reverse direction');
 
 
 // --------------------
 // currentIteration
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2s'});
-  var effect = div.getAnimations()[0].effect;
+
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 2s' });
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().currentIteration, null,
                 'Initial value of currentIteration before phase');
 }, 'currentIteration of a new animation with no backwards fill is unresolved ' +
    'in before phase');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
+  const anim = div.getAnimations()[0];
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
 }, 'currentIteration of a new animation is zero');
 
-test(function(t) {
+test(t => {
   // Note: FillMode here is "both" because
   // 1. Since this a zero-duration animation, it will already have finished
   //    so it won't be returned by getAnimations() unless it fills forwards.
   // 2. Fill backwards, so the currentIteration (before phase) wouldn't be
   //    unresolved (null value).
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s infinite both'});
-  var anim = div.getAnimations()[0];
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity,
                 'Initial value of currentIteration in after phase');
 
   // Seek backwards
   anim.currentTime -= 2 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Value of currentIteration count during before phase');
 }, 'currentIteration of an infinitely repeating zero-duration animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.5 both'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 0s 10.5 both' });
+  const anim = div.getAnimations()[0];
 
   // Note: currentIteration = ceil(iteration start + iteration count) - 1
   assert_equals(anim.effect.getComputedTiming().currentIteration, 10,
                 'Initial value of currentIteration');
 
   // Seek backwards
   anim.currentTime -= 2 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Value of currentIteration count during before phase');
 }, 'currentIteration of a finitely repeating zero-duration animation');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 5.5 forwards'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, {
+    style: 'animation: moveAnimation 100s 5.5 forwards'
+  });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
   // The 3rd iteration
   // Note: currentIteration = floor(scaled active time / iteration duration)
   anim.currentTime = 250 * MS_PER_SEC;
   assert_equals(anim.effect.getComputedTiming().currentIteration, 2,
                 'Value of currentIteration during the 3rd iteration');
   // Finish
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().currentIteration, 5,
                 'Value of currentIteration in after phase');
 }, 'currentIteration of an animation with a non-integral iteration count');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2 forwards'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s 2 forwards' });
+  const anim = div.getAnimations()[0];
 
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
   // Finish
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().currentIteration, 1,
                 'Value of currentIteration in after phase');
 }, 'currentIteration of an animation with an integral iteration count');
 
-test(function(t) {
-  var div = addDiv(t, {style: 'animation: moveAnimation 100s forwards'});
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: moveAnimation 100s forwards' });
+  const anim = div.getAnimations()[0];
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
   // Finish
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
                 'Value of currentIteration in after phase');
 }, 'currentIteration of an animation with a default iteration count');
 
-test(function(t) {
-  var div = addDiv(t);
-  var effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
+test(t => {
+  const div = addDiv(t);
+  const effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
 
   assert_equals(effect.getComputedTiming().currentIteration, null,
                 'currentIteration for orphaned effect');
 }, 'currentIteration of an AnimationEffect without an Animation');
 
 // TODO: If iteration duration is Infinity, currentIteration is 0.
 // However, we cannot set iteration duration to Infinity in CSS Animation now.
 
rename from dom/animation/test/css-animations/test_animation-currenttime.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html
--- a/dom/animation/test/css-animations/test_animation-currenttime.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html
@@ -1,347 +1,74 @@
 <!doctype html>
 <html>
-  <head>
-    <meta charset=utf-8>
-    <title>Tests for the effect of setting a CSS animation's
-           Animation.currentTime</title>
-    <style>
+<head>
+<meta charset=utf-8>
+<title>CSSAnimation.currentTime</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
+<style>
 
 .animated-div {
   margin-left: 10px;
   /* Make it easier to calculate expected values: */
   animation-timing-function: linear ! important;
 }
 
 @keyframes anim {
   from { margin-left: 100px; }
   to { margin-left: 200px; }
 }
 
-    </style>
-    <script src="/resources/testharness.js"></script>
-    <script src="/resources/testharnessreport.js"></script>
-    <script src="../testcommon.js"></script>
-  </head>
-  <body>
-    <div id="log"></div>
-    <script type="text/javascript">
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
 
 'use strict';
 
-// TODO: We should separate this test(Testing for CSS Animation events /
-// Testing for currentTime of Web Animation).
-// e.g:
-//  CSS Animation events test :
-//    - check the firing an event using Animation.currentTime
-//  The current Time of Web Animation test :
-//    - check an current time value on several situation(init / processing..)
-//    - Based on W3C Spec, check the behavior of setting current time.
-
-// TODO: Once the computedTiming property is implemented, add checks to the
-// checker helpers to ensure that computedTiming's properties are updated as
-// expected.
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
-
-const CSS_ANIM_EVENTS =
-  ['animationstart', 'animationiteration', 'animationend'];
-
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = "anim 100s";
-  var animation = div.getAnimations()[0];
-
-  // Animations shouldn't start until the next paint tick, so:
-  assert_equals(animation.currentTime, 0,
-    'Animation.currentTime should be zero when an animation ' +
-    'is initially created');
-
-  // Make sure the animation is running before we set the current time.
-  animation.startTime = animation.timeline.currentTime;
-
-  animation.currentTime = 50 * MS_PER_SEC;
-  assert_time_equals_literal(animation.currentTime, 50 * MS_PER_SEC,
-    'Check setting of currentTime actually works');
-}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
-   'currentTime');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    // the 0.0001 here is for rounding error
-    assert_less_than_equal(animation.currentTime,
-      animation.timeline.currentTime - animation.startTime + 0.0001,
-      'Animation.currentTime should be less than the local time ' +
-      'equivalent of the timeline\'s currentTime on the first paint tick ' +
-      'after animation creation');
+promise_test(async t => {
+  const div = addDiv(t, { class: 'animated-div' });
+  div.style.animation = 'anim 100s';
+  const animation = div.getAnimations()[0];
 
-    animation.currentTime = 100 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationstart');
-  }).then(function() {
-    animation.currentTime = 200 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationend');
-  });
-}, 'Skipping forward through animation');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-  animation.currentTime = 200 * MS_PER_SEC;
-  var previousTimelineTime = animation.timeline.currentTime;
-
-  return eventWatcher.wait_for(['animationstart',
-                                'animationend']).then(function() {
-    assert_true(document.timeline.currentTime - previousTimelineTime <
-                100 * MS_PER_SEC,
-                'Sanity check that seeking worked rather than the events ' +
-                'firing after normal playback through the very long ' +
-                'animation duration');
+  assert_equals(
+    animation.currentTime,
+    0,
+    'Animation.currentTime should be zero when an animation ' +
+      'is initially created'
+  );
 
-    animation.currentTime = 150 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationstart');
-  }).then(function() {
-    animation.currentTime = 0;
-    return eventWatcher.wait_for('animationend');
-  });
-}, 'Skipping backwards through animation');
-
-// Next we have multiple tests to check that redundant currentTime changes do
-// NOT dispatch events. It's impossible to distinguish between events not being
-// dispatched and events just taking an incredibly long time to dispatch
-// without waiting an infinitely long time. Obviously we don't want to do that
-// (block this test from finishing forever), so instead we just listen for
-// events until two animation frames (i.e. requestAnimationFrame callbacks)
-// have happened, then assume that no events will ever be dispatched for the
-// redundant changes if no events were detected in that time.
+  await animation.ready;
 
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  animation.currentTime = 150 * MS_PER_SEC;
-  animation.currentTime = 50 * MS_PER_SEC;
-
-  return waitForAnimationFrames(2);
-}, 'Redundant change, before -> active, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  animation.currentTime = 250 * MS_PER_SEC;
   animation.currentTime = 50 * MS_PER_SEC;
 
-  return waitForAnimationFrames(2);
-}, 'Redundant change, before -> after, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise = eventWatcher.wait_for('animationstart').then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    animation.currentTime = 150 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.currentTime = 150 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, active -> before, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise = eventWatcher.wait_for('animationstart').then(function() {
-    animation.currentTime = 250 * MS_PER_SEC;
-    animation.currentTime = 150 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.currentTime = 150 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, active -> after, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise =  eventWatcher.wait_for(['animationstart',
-                                           'animationend']).then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    animation.currentTime = 250 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.currentTime = 250 * MS_PER_SEC;
+  assert_time_equals_literal(
+    animation.currentTime,
+    50 * MS_PER_SEC,
+    'Check setting of currentTime actually works'
+  );
+  assert_equals(getComputedStyle(div).marginLeft, '150px');
+}, 'currentTime can be used to seek a CSS animation');
 
-  return retPromise;
-}, 'Redundant change, after -> before, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise =  eventWatcher.wait_for(['animationstart',
-                                           'animationend']).then(function() {
-    animation.currentTime = 150 * MS_PER_SEC;
-    animation.currentTime = 250 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.currentTime = 250 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, after -> active, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s"
-  var animation = div.getAnimations()[0];
-
-  animation.pause();
-  animation.currentTime = 150 * MS_PER_SEC;
-
-  return eventWatcher.wait_for(['animationstart',
-                                'animationend']).then(function() {
-    animation.currentTime = 50 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationstart');
-  });
-}, 'Seeking finished -> paused dispatches animationstart');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = "anim 100s";
-
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    var exception;
-    try {
-      animation.currentTime = null;
-    } catch (e) {
-      exception = e;
-    }
-    assert_equals(exception.name, 'TypeError',
-      'Expect TypeError exception on trying to set ' +
-      'Animation.currentTime to null');
-  });
-}, 'Setting currentTime to null');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+promise_test(async t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.animation = 'anim 100s';
 
-  var animation = div.getAnimations()[0];
-  var pauseTime;
-
-  return animation.ready.then(function() {
-    assert_not_equals(animation.currentTime, null,
-      'Animation.currentTime not null on ready Promise resolve');
-    animation.pause();
-    return animation.ready;
-  }).then(function() {
-    pauseTime = animation.currentTime;
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(animation.currentTime, pauseTime,
-      'Animation.currentTime is unchanged after pausing');
-  });
-}, 'Animation.currentTime after pausing');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = "anim 100s";
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    // just before animation ends:
-    animation.currentTime = 100 * MS_PER_SEC - 1;
-    return waitForAnimationFrames(2);
-  }).then(function() {
-    assert_equals(animation.currentTime, 100 * MS_PER_SEC,
-      'Animation.currentTime should not continue to increase after the ' +
-      'animation has finished');
-  });
-}, 'Animation.currentTime clamping');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = "anim 100s";
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    // play backwards:
-    animation.playbackRate = -1;
+  const animation = div.getAnimations()[0];
+  await animation.ready;
 
-    // just before animation ends (at the "start"):
-    animation.currentTime = 1;
-
-    return waitForAnimationFrames(2);
-  }).then(function() {
-    assert_equals(animation.currentTime, 0,
-      'Animation.currentTime should not continue to decrease after an ' +
-      'animation running in reverse has finished and currentTime is zero');
-  });
-}, 'Animation.currentTime clamping for reversed animation');
-
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
-  animation.cancel();
-
-  assert_equals(animation.currentTime, null,
-                'The currentTime of a cancelled animation should be null');
-}, 'Animation.currentTime after cancelling');
+  assert_throws(
+    new TypeError(),
+    () => {
+      animation.currentTime = null;
+    },
+    'Expect TypeError exception on trying to set Animation.currentTime to null'
+  );
+}, 'Setting currentTime to null on a CSS animation throws');
 
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    animation.finish();
-
-    // Initiate a pause then abort it
-    animation.pause();
-    animation.play();
-
-    // Wait to return to running state
-    return animation.ready;
-  }).then(function() {
-    assert_true(animation.currentTime < 100 * 1000,
-                'After aborting a pause when finished, the currentTime should'
-                + ' jump back towards the start of the animation');
-  });
-}, 'After aborting a pause when finished, the call to play() should rewind'
-   + ' the current time');
-
-    </script>
-  </body>
+</script>
+</body>
 </html>
rename from dom/animation/test/css-animations/test_animation-id.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html
--- a/dom/animation/test/css-animations/test_animation-id.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html
@@ -1,26 +1,29 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.id</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes abc { }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'abc 100s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(animation.id, '', 'id for CSS Animation is initially empty');
+
   animation.id = 'anim'
-
   assert_equals(animation.id, 'anim', 'animation.id reflects the value set');
 }, 'Animation.id for CSS Animations');
 
 </script>
 </body>
 </html>
rename from dom/animation/test/css-animations/test_animation-pausing.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html
--- a/dom/animation/test/css-animations/test_animation-pausing.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html
@@ -1,168 +1,172 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Pausing a CSSAnimation</title>
+<link rel="help"
+      href="https://drafts.csswg.org/css-animations-2/#animation-play-state">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim {
   0% { margin-left: 0px }
   100% { margin-left: 10000px }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-function getMarginLeft(cs) {
-  return parseFloat(cs.marginLeft);
-}
+const getMarginLeft = cs => parseFloat(cs.marginLeft);
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t);
+  const cs = getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
   animation.play();
 
-  return animation.ready.then(waitForNextFrame).then(function() {
-    assert_greater_than(getMarginLeft(cs), 0,
-                        'Playing value of margin-left is greater than zero');
-  });
+  await animation.ready;
+  await waitForNextFrame();
+
+  assert_greater_than(getMarginLeft(cs), 0,
+                      'Playing value of margin-left is greater than zero');
 }, 'play() overrides animation-play-state');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t);
+  const cs = getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   animation.pause();
   div.style.animationPlayState = 'running';
 
-  return animation.ready.then(waitForNextFrame).then(function() {
-    assert_equals(cs.animationPlayState, 'running',
-                  'animation-play-state is running');
-    assert_equals(getMarginLeft(cs), 0,
-                  'Paused value of margin-left is zero');
-  });
+  await animation.ready;
+  await waitForNextFrame();
+
+  assert_equals(cs.animationPlayState, 'running',
+                'animation-play-state is running');
+  assert_equals(getMarginLeft(cs), 0,
+                'Paused value of margin-left is zero');
 }, 'pause() overrides animation-play-state');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t);
+  const cs = getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
   animation.play();
-  var previousAnimVal;
+
+  await animation.ready;
+
+  div.style.animationPlayState = 'running';
+  cs.animationPlayState; // Trigger style resolution
+  await waitForNextFrame();
 
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'running';
-    cs.animationPlayState; // Trigger style resolution
-    return waitForNextFrame();
-  }).then(function() {
-    assert_equals(cs.animationPlayState, 'running',
-                  'animation-play-state is running');
-    div.style.animationPlayState = 'paused';
-    return animation.ready;
-  }).then(function() {
-    assert_equals(cs.animationPlayState, 'paused',
-                  'animation-play-state is paused');
-    previousAnimVal = getMarginLeft(cs);
-    return waitForNextFrame();
-  }).then(function() {
-    assert_equals(getMarginLeft(cs), previousAnimVal,
-                  'Animated value of margin-left does not change when'
-                  + ' paused by style');
-  });
+  assert_equals(cs.animationPlayState, 'running',
+                'animation-play-state is running');
+  div.style.animationPlayState = 'paused';
+  await animation.ready;
+
+  assert_equals(cs.animationPlayState, 'paused',
+                'animation-play-state is paused');
+  const previousAnimVal = getMarginLeft(cs);
+  await waitForNextFrame();
+
+  assert_equals(getMarginLeft(cs), previousAnimVal,
+                'Animated value of margin-left does not change when'
+                + ' paused by style');
 }, 'play() is overridden by later setting "animation-play-state: paused"');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t);
+  const cs = getComputedStyle(div);
   div.style.animation = 'anim 1000s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   // Set the specified style first. If implementations fail to
   // apply the style changes first, they will ignore the redundant
   // call to play() and fail to correctly override the pause style.
   div.style.animationPlayState = 'paused';
   animation.play();
-  var previousAnimVal = getMarginLeft(cs);
+  const previousAnimVal = getMarginLeft(cs);
+
+  await animation.ready;
+  await waitForNextFrame();
 
-  return animation.ready.then(waitForNextFrame).then(function() {
-    assert_equals(cs.animationPlayState, 'paused',
-                  'animation-play-state is paused');
-    assert_greater_than(getMarginLeft(cs), previousAnimVal,
-                        'Playing value of margin-left is increasing');
-  });
+  assert_equals(cs.animationPlayState, 'paused',
+                'animation-play-state is paused');
+  assert_greater_than(getMarginLeft(cs), previousAnimVal,
+                      'Playing value of margin-left is increasing');
 }, 'play() flushes pending changes to animation-play-state first');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t);
+  const cs = getComputedStyle(div);
   div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(getMarginLeft(cs), 0,
                 'Initial value of margin-left is zero');
 
   // Unlike the previous test for play(), since calling pause() is sticky,
   // we'll apply it even if the underlying style also says we're paused.
   //
   // We would like to test that implementations flush styles before running
   // pause() but actually there's no style we can currently set that will
   // change the behavior of pause(). That may change in the future
   // (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
   //
   // For now this just serves as a sanity check that we do the same thing
   // even if we set style before calling the API.
   div.style.animationPlayState = 'running';
   animation.pause();
-  var previousAnimVal = getMarginLeft(cs);
+  const previousAnimVal = getMarginLeft(cs);
+
+  await animation.ready;
+  await waitForNextFrame();
 
-  return animation.ready.then(waitForNextFrame).then(function() {
-    assert_equals(cs.animationPlayState, 'running',
-                  'animation-play-state is running');
-    assert_equals(getMarginLeft(cs), previousAnimVal,
-                  'Paused value of margin-left does not change');
-  });
+  assert_equals(cs.animationPlayState, 'running',
+                'animation-play-state is running');
+  assert_equals(getMarginLeft(cs), previousAnimVal,
+                'Paused value of margin-left does not change');
 }, 'pause() applies pending changes to animation-play-state first');
 // (Note that we can't actually test for this; see comment above, in test-body.)
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: anim 1000s' });
-  var animation = div.getAnimations()[0];
-  var readyPromiseRun = false;
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: anim 1000s' });
+  const animation = div.getAnimations()[0];
+  let readyPromiseRun = false;
 
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-    assert_true(animation.pending && animation.playState === 'paused',
-                'Animation is pause-pending');
+  await animation.ready;
+
+  div.style.animationPlayState = 'paused';
+  assert_true(animation.pending && animation.playState === 'paused',
+              'Animation is pause-pending');
 
-    // Set current time
-    animation.currentTime = 5 * MS_PER_SEC;
-    assert_equals(animation.playState, 'paused',
-                  'Animation is paused immediately after setting currentTime');
-    assert_equals(animation.startTime, null,
-                  'Animation startTime is unresolved immediately after ' +
-                  'setting currentTime');
-    assert_equals(animation.currentTime, 5 * MS_PER_SEC,
-                  'Animation currentTime does not change when forcing a ' +
-                  'pause operation to complete');
+  // Set current time
+  animation.currentTime = 5 * MS_PER_SEC;
+  assert_equals(animation.playState, 'paused',
+                'Animation is paused immediately after setting currentTime');
+  assert_equals(animation.startTime, null,
+                'Animation startTime is unresolved immediately after ' +
+                'setting currentTime');
+  assert_equals(animation.currentTime, 5 * MS_PER_SEC,
+                'Animation currentTime does not change when forcing a ' +
+                'pause operation to complete');
 
-    // The ready promise should now be resolved. If it's not then test will
-    // probably time out before anything else happens that causes it to resolve.
-    return animation.ready;
-  });
+  // The ready promise should now be resolved. If it's not then test will
+  // probably time out before anything else happens that causes it to resolve.
+  await animation.ready;
 }, 'Setting the current time completes a pending pause');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-playstate.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html
--- a/dom/animation/test/css-animations/test_animation-playstate.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html
@@ -1,61 +1,57 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.playState</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim { }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
-  div.style.animation = 'anim 1000s';
-  var animation = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { 'style': 'animation: anim 100s' });
+  const animation = div.getAnimations()[0];
+  assert_true(animation.pending);
   assert_equals(animation.playState, 'running');
-}, 'Animation returns correct playState when running');
+  assert_equals(animation.startTime, null);
+}, 'A new CSS animation is initially play-pending');
 
-test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
-  div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { 'style': 'animation: anim 1000s paused' });
+  const animation = div.getAnimations()[0];
   assert_equals(animation.playState, 'paused');
 }, 'Animation returns correct playState when paused');
 
-test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
-  div.style.animation = 'anim 1000s';
-  var animation = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { 'style': 'animation: anim 1000s' });
+  const animation = div.getAnimations()[0];
   animation.pause();
   assert_equals(animation.playState, 'paused');
 }, 'Animation.playState updates when paused by script');
 
-test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
-  div.style.animation = 'anim 1000s paused';
-  var animation = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { 'style': 'animation: anim 1000s paused' });
+  const animation = div.getAnimations()[0];
   div.style.animationPlayState = 'running';
 
   // This test also checks that calling playState flushes style
   assert_equals(animation.playState, 'running',
                 'Animation.playState reports running after updating'
                 + ' animation-play-state (got: ' + animation.playState + ')');
 }, 'Animation.playState updates when resumed by setting style');
 
-test(function(t) {
-  var div = addDiv(t);
-  div.style.animation = 'anim 1000s';
-  var animation = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { 'style': 'animation: anim 1000s' });
+  const animation = div.getAnimations()[0];
   animation.cancel();
   assert_equals(animation.playState, 'idle');
-}, 'Animation returns correct playState when cancelled');
+}, 'Animation returns correct playState when canceled');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-ready.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html
--- a/dom/animation/test/css-animations/test_animation-ready.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html
@@ -1,149 +1,100 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.ready</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes abc {
   to { transform: translate(10px) }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'abc 100s paused';
-  var animation = div.getAnimations()[0];
-  var originalReadyPromise = animation.ready;
+  const animation = div.getAnimations()[0];
+  const originalReadyPromise = animation.ready;
 
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'running';
-    assert_not_equals(animation.ready, originalReadyPromise,
-                      'After updating animation-play-state a new ready promise'
-                      + ' object is created');
-  });
+  await animation.ready;
+
+  div.style.animationPlayState = 'running';
+  assert_not_equals(animation.ready, originalReadyPromise,
+                    'After updating animation-play-state a new ready promise'
+                    + ' object is created');
 }, 'A new ready promise is created when setting animation-play-state: running');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
   // Set up pending animation
   div.style.animation = 'abc 100s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_true(animation.pending, 'Animation is initially pending');
+  const readyPromise = animation.ready;
 
-  // Set up listeners on ready promise
-  var retPromise = animation.ready.then(function() {
-    assert_unreached('ready promise is fulfilled');
-  }).catch(function(err) {
-    assert_equals(err.name, 'AbortError',
-                  'ready promise is rejected with AbortError');
-  });
-
-  // Now cancel the animation and flush styles
+  // Cancel the animation and flush styles
   div.style.animation = '';
   getComputedStyle(div).animation;
 
-  return retPromise;
+  await promise_rejects(t, 'AbortError', readyPromise,
+                        'ready promise is rejected with AbortError');
 }, 'ready promise is rejected when an animation is canceled by resetting'
    + ' the animation property');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
   // As before, but this time instead of removing all animations, simply update
   // the list of animations. At least for Firefox, updating is a different
   // code path.
 
   // Set up pending animation
   div.style.animation = 'abc 100s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_true(animation.pending, 'Animation is initially pending');
+  const readyPromise = animation.ready;
 
-  // Set up listeners on ready promise
-  var retPromise = animation.ready.then(function() {
-    assert_unreached('ready promise was fulfilled');
-  }).catch(function(err) {
-    assert_equals(err.name, 'AbortError',
-                  'ready promise is rejected with AbortError');
-  });
-
-  // Now update the animation and flush styles
+  // Update the animation and flush styles
   div.style.animation = 'def 100s';
   getComputedStyle(div).animation;
 
-  return retPromise;
-}, 'ready promise is rejected when an animation is cancelled by updating'
+  await promise_rejects(t, 'AbortError', readyPromise,
+                        'ready promise is rejected with AbortError');
+}, 'ready promise is rejected when an animation is canceled by updating'
    + ' the animation property');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: abc 100s' });
-  var animation = div.getAnimations()[0];
-  var originalReadyPromise = animation.ready;
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: abc 100s' });
+  const animation = div.getAnimations()[0];
+  const originalReadyPromise = animation.ready;
 
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-    assert_not_equals(animation.ready, originalReadyPromise,
-                      'A new Promise object is generated when setting'
-                      + ' animation-play-state: paused');
-  });
+  await animation.ready;
+
+  div.style.animationPlayState = 'paused';
+  assert_not_equals(animation.ready, originalReadyPromise,
+                    'A new Promise object is generated when setting'
+                    + ' animation-play-state: paused');
 }, 'A new ready promise is created when setting animation-play-state: paused');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: abc 100s' });
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, { style: 'animation: abc 100s' });
+  const animation = div.getAnimations()[0];
 
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-    var firstReadyPromise = animation.ready;
-    animation.pause();
-    assert_equals(animation.ready, firstReadyPromise,
-                  'Ready promise objects are identical after redundant pause');
-  });
-}, 'Pausing twice re-uses the same Promise');
-
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: abc 100s' });
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-
-    // Flush style and verify we're pending at the same time
-    assert_true(animation.pending, 'Animation is pending');
-    var pauseReadyPromise = animation.ready;
+  await animation.ready;
 
-    // Now play again immediately
-    div.style.animationPlayState = 'running';
-    assert_true(animation.pending, 'Animation is still pending');
-    assert_equals(animation.ready, pauseReadyPromise,
-                  'The pause Promise is re-used when playing while waiting'
-                  + ' to pause');
-
-    return animation.ready;
-  }).then(function() {
-    assert_true(!animation.pending && animation.playState === 'running',
-                'Animation is running after aborting a pause');
-  });
-}, 'If a pause operation is interrupted, the ready promise is reused');
-
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: abc 100s' });
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-    return animation.ready;
-  }).then(function(resolvedAnimation) {
-    assert_equals(resolvedAnimation, animation,
-                  'Promise received when ready Promise for a pause operation'
-                  + ' is completed is the animation on which the pause was'
-                  + ' performed');
-  });
-}, 'When a pause is complete the Promise callback gets the correct animation');
+  div.style.animationPlayState = 'paused';
+  const firstReadyPromise = animation.ready;
+  animation.pause();
+  assert_equals(animation.ready, firstReadyPromise,
+                'Ready promise objects are identical after redundant pause');
+}, 'Pausing twice re-uses the same Promise');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_animation-starttime.html
rename to testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html
--- a/dom/animation/test/css-animations/test_animation-starttime.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html
@@ -1,385 +1,75 @@
 <!doctype html>
 <html>
-  <head>
-    <meta charset=utf-8>
-    <title>Tests for the effect of setting a CSS animation's
-           Animation.startTime</title>
-    <style>
+<head>
+<meta charset=utf-8>
+<title>CSSAnimation.startTime</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
+<style>
 
 .animated-div {
   margin-left: 10px;
   /* Make it easier to calculate expected values: */
   animation-timing-function: linear ! important;
 }
 
 @keyframes anim {
   from { margin-left: 100px; }
   to { margin-left: 200px; }
 }
 
-    </style>
-    <script src="/resources/testharness.js"></script>
-    <script src="/resources/testharnessreport.js"></script>
-    <script src="../testcommon.js"></script>
-  </head>
-  <body>
-    <div id="log"></div>
-    <script type="text/javascript">
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
 
 'use strict';
 
-// TODO: We should separate this test(Testing for CSS Animation events /
-// Testing for start time of Web Animation).
-// e.g:
-//  CSS Animation events test:
-//    - check the firing an event after setting an Animation.startTime
-//  The start time of Web Animation test:
-//    - check an start time value on several situation(init / processing..)
-//    - Based on W3C Spec, check the behavior of setting current time.
-
-// TODO: Once the computedTiming property is implemented, add checks to the
-// checker helpers to ensure that computedTiming's properties are updated as
-// expected.
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
-
-const CSS_ANIM_EVENTS =
-  ['animationstart', 'animationiteration', 'animationend'];
-
-test(function(t)
-{
-  var div = addDiv(t, { 'style': 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-
-  assert_equals(animation.startTime, null, 'startTime is unresolved');
-}, 'startTime of a newly created (play-pending) animation is unresolved');
-
-test(function(t)
-{
-  var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
-  var animation = div.getAnimations()[0];
-  assert_equals(animation.startTime, null, 'startTime is unresolved');
-}, 'startTime of a newly created (pause-pending) animation is unresolved');
-
-promise_test(function(t)
-{
-  var div = addDiv(t, { 'style': 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    assert_true(animation.startTime > 0,
-                'startTime is resolved when running');
-  });
-}, 'startTime is resolved when running');
-
-promise_test(function(t)
-{
-  var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    assert_equals(animation.startTime, null,
-                  'startTime is unresolved when paused');
-  });
-}, 'startTime is unresolved when paused');
+test(t => {
+  const div = addDiv(t, { 'class': 'animated-div' });
+  div.style.animation = 'anim 100s 100s';
+  const animation = div.getAnimations()[0];
 
-promise_test(function(t)
-{
-  var div = addDiv(t, { 'style': 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    div.style.animationPlayState = 'paused';
-    getComputedStyle(div).animationPlayState;
-
-    assert_not_equals(animation.startTime, null,
-                      'startTime is resolved when pause-pending');
-
-    div.style.animationPlayState = 'running';
-    getComputedStyle(div).animationPlayState;
-
-    assert_not_equals(animation.startTime, null,
-                      'startTime is preserved when a pause is aborted');
-  });
-}, 'startTime while pause-pending and play-pending');
-
-promise_test(function(t) {
-  var div = addDiv(t, { 'style': 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-  // Seek to end to put us in the finished state
-  animation.currentTime = 100 * MS_PER_SEC;
-
-  return animation.ready.then(function() {
-    // Call play() which puts us back in the running state
-    animation.play();
-
-    assert_equals(animation.startTime, null, 'startTime is unresolved');
-  });
-}, 'startTime while play-pending from finished state');
-
-test(function(t) {
-  var div = addDiv(t, { 'style': 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-  animation.finish();
-  // Call play() which puts us back in the running state
-  animation.play();
-
-  assert_equals(animation.startTime, null, 'startTime is unresolved');
-}, 'startTime while play-pending from finished state using finish()');
-
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'animation: anim 100s' });
-  var animation = div.getAnimations()[0];
-
-  assert_equals(animation.startTime, null, 'The initial startTime is null');
-  var initialTimelineTime = document.timeline.currentTime;
-
-  return animation.ready.then(function() {
-    assert_true(animation.startTime > initialTimelineTime,
-                'After the animation has started, startTime is greater than ' +
-                'the time when it was started');
-    var startTimeBeforePausing = animation.startTime;
-
-    div.style.animationPlayState = 'paused';
-    // Flush styles just in case querying animation.startTime doesn't flush
-    // styles (which would be a bug in of itself and could mask a further bug
-    // by causing startTime to appear to not change).
-    getComputedStyle(div).animationPlayState;
+  const timelineTime = animation.timeline.currentTime;
+  animation.startTime = timelineTime;
 
-    assert_equals(animation.startTime, startTimeBeforePausing,
-                  'The startTime does not change when pausing-pending');
-    return animation.ready;
-  }).then(function() {
-    assert_equals(animation.startTime, null,
-                  'After actually pausing, the startTime of an animation ' +
-                  'is null');
-  });
-}, 'Pausing should make the startTime become null');
-
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s 100s';
-  var animation = div.getAnimations()[0];
-  var currentTime = animation.timeline.currentTime;
-  animation.startTime = currentTime;
-
-  assert_times_equal(animation.startTime, currentTime,
+  assert_times_equal(animation.startTime, timelineTime,
     'Check setting of startTime actually works');
-}, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
-   'startTime');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = 'anim 100s 100s';
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
-      'Animation.startTime should be less than the timeline\'s ' +
-      'currentTime on the first paint tick after animation creation');
-
-    animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationstart');
-  }).then(function() {
-    animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
-    return eventWatcher.wait_for('animationend');
-  });
-}, 'Skipping forward through animation');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = 'anim 100s 100s';
-  var animation = div.getAnimations()[0];
-  animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
-  var previousTimelineTime = animation.timeline.currentTime;
-
-  return eventWatcher.wait_for(['animationstart',
-                                'animationend']).then(function() {
-    assert_true(document.timeline.currentTime - previousTimelineTime <
-                  100 * MS_PER_SEC,
-                'Sanity check that seeking worked rather than the events ' +
-                'firing after normal playback through the very long ' +
-                'animation duration');
-
-    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
+}, 'The start time of a CSS animation can be set');
 
-    // Despite going backwards from after the end of the animation (to being
-    // in the active interval), we now expect an 'animationstart' event
-    // because the animation should go from being inactive to active.
-    return eventWatcher.wait_for('animationstart');
-  }).then(function() {
-    animation.startTime = animation.timeline.currentTime;
-
-    // Despite going backwards from just after the active interval starts to
-    // the animation start time, we now expect an animationend event
-    // because we went from inside to outside the active interval.
-    return eventWatcher.wait_for('animationend');
-  }).then(function() {
-    assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
-      'Animation.startTime should be less than the timeline\'s ' +
-      'currentTime on the first paint tick after animation creation');
-  });
-}, 'Skipping backwards through animation');
-
-// Next we have multiple tests to check that redundant startTime changes do NOT
-// dispatch events. It's impossible to distinguish between events not being
-// dispatched and events just taking an incredibly long time to dispatch
-// without waiting an infinitely long time. Obviously we don't want to do that
-// (block this test from finishing forever), so instead we just listen for
-// events until two animation frames (i.e. requestAnimationFrame callbacks)
-// have happened, then assume that no events will ever be dispatched for the
-// redundant changes if no events were detected in that time.
+promise_test(async t => {
+  const div = addDiv(t, { 'class': 'animated-div' });
+  div.style.animation = 'anim 100s 100s';
+  const animation = div.getAnimations()[0];
 
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
-  animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
-
-  return waitForAnimationFrames(2);
-}, 'Redundant change, before -> active, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-  animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
-
-  return waitForAnimationFrames(2);
-}, 'Redundant change, before -> after, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise =  eventWatcher.wait_for('animationstart').then(function() {
-    animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
-    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
+  // Seek to the half-way point
   animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
 
-  return retPromise;
-}, 'Redundant change, active -> before, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise = eventWatcher.wait_for('animationstart').then(function() {
-    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, active -> after, then back');
+  assert_equals(getComputedStyle(div).marginLeft, '150px');
+}, 'The start time can be set to seek a CSS animation');
 
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise = eventWatcher.wait_for(['animationstart',
-                                          'animationend']).then(function() {
-    animation.startTime = animation.timeline.currentTime - 50 * MS_PER_SEC;
-    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
-  });
-  // get us into the initial state:
-  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, after -> before, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
-  div.style.animation = "anim 100s 100s";
-  var animation = div.getAnimations()[0];
-
-  var retPromise = eventWatcher.wait_for(['animationstart',
-                                          'animationend']).then(function() {
-    animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
-    animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-
-    return waitForAnimationFrames(2);
+promise_test(async t => {
+  const div = addDiv(t, { class: 'animated-div' });
+  const eventWatcher = new EventWatcher(t, div, [
+    'animationstart',
+    'animationend',
+  ]);
+  div.style.animation = 'anim 100s 100s';
+  const animation = div.getAnimations()[0];
 
-  });
-  // get us into the initial state:
-  animation.startTime = animation.timeline.currentTime - 250 * MS_PER_SEC;
-
-  return retPromise;
-}, 'Redundant change, after -> active, then back');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s 100s';
-  var animation = div.getAnimations()[0];
-  var storedCurrentTime;
+  await animation.ready;
 
-  return animation.ready.then(function() {
-    storedCurrentTime = animation.currentTime;
-    animation.startTime = null;
-    return animation.ready;
-  }).then(function() {
-    assert_equals(animation.currentTime, storedCurrentTime,
-      'Test that hold time is correct');
-  });
-}, 'Setting startTime to null');
-
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
+  animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
+  await eventWatcher.wait_for('animationstart');
 
-  return animation.ready.then(function() {
-    var savedStartTime = animation.startTime;
-
-    assert_not_equals(animation.startTime, null,
-      'Animation.startTime not null on ready Promise resolve');
-
-    animation.pause();
-    return animation.ready;
-  }).then(function() {
-    assert_equals(animation.startTime, null,
-      'Animation.startTime is null after paused');
-    assert_equals(animation.playState, 'paused',
-      'Animation.playState is "paused" after pause() call');
-  });
-}, 'Animation.startTime after pausing');
+  animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
+  await eventWatcher.wait_for('animationend');
+}, 'Seeking a CSS animation using the start time dispatches animation events');
 
-promise_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
-
-  return animation.ready.then(function() {
-    animation.cancel();
-    assert_equals(animation.startTime, null,
-                  'The startTime of a cancelled animation should be null');
-  });
-}, 'Animation.startTime after cancelling');
-
-    </script>
-  </body>
+</script>
+</body>
 </html>
rename from dom/animation/test/css-animations/test_pseudoElement-get-animations.html
rename to testing/web-platform/tests/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
--- a/dom/animation/test/css-animations/test_pseudoElement-get-animations.html
+++ b/testing/web-platform/tests/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
@@ -1,13 +1,16 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSPseudoElement.getAnimations() for CSS animations</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim1 { }
 @keyframes anim2 { }
 .before::before {
   animation: anim1 10s;
   content: '';
 }
 .after-with-mix-anims-trans::after {
@@ -23,52 +26,52 @@
   content: '';
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t, { class: 'before' });
-  var pseudoTarget = document.getAnimations()[0].effect.target;
+test(t => {
+  const div = addDiv(t, { class: 'before' });
+  const pseudoTarget = document.getAnimations()[0].effect.target;
   assert_equals(pseudoTarget.getAnimations().length, 1,
                 'Expected number of animations are returned');
   assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
                 'CSS animation name matches');
 }, 'getAnimations returns CSSAnimation objects');
 
-test(function(t) {
-  var div = addDiv(t, { class: 'after-with-mix-anims-trans' });
+test(t => {
+  const div = addDiv(t, { class: 'after-with-mix-anims-trans' });
   // Trigger transitions
   flushComputedStyle(div);
   div.classList.add('after-change');
 
   // Create additional animation on the pseudo-element from script
-  var pseudoTarget = document.getAnimations()[0].effect.target;
-  var effect = new KeyframeEffect(pseudoTarget,
-                                  { background: ["blue", "red"] },
-                                  3 * MS_PER_SEC);
-  var newAnimation = new Animation(effect, document.timeline);
+  const pseudoTarget = document.getAnimations()[0].effect.target;
+  const effect = new KeyframeEffect(pseudoTarget,
+                                    { background: ["blue", "red"] },
+                                    3 * MS_PER_SEC);
+  const newAnimation = new Animation(effect, document.timeline);
   newAnimation.id = 'scripted-anim';
   newAnimation.play();
 
   // Check order - the script-generated animation should appear later
-  var anims = pseudoTarget.getAnimations();
+  const anims = pseudoTarget.getAnimations();
   assert_equals(anims.length, 5,
                 'Got expected number of animations/trnasitions running on ' +
                 '::after pseudo element');
   assert_equals(anims[0].transitionProperty, 'height',
                 '1st animation is the 1st transition sorted by name');
   assert_equals(anims[1].transitionProperty, 'width',
                 '2nd animation is the 2nd transition sorted by name ');
   assert_equals(anims[2].animationName, 'anim1',
                 '3rd animation is the 1st animation in animation-name list');
   assert_equals(anims[3].animationName, 'anim2',
                 '4rd animation is the 2nd animation in animation-name list');
   assert_equals(anims[4].id, 'scripted-anim',
                 'Animation added by script appears last');
-}, 'getAnimations returns css transitions/animations, and script-generated ' +
+}, 'getAnimations returns CSS transitions/animations, and script-generated ' +
    'animations in the expected order');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_document-get-animations.html
rename to testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
--- a/dom/animation/test/css-animations/test_document-get-animations.html
+++ b/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
@@ -1,13 +1,15 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Document.getAnimations() for CSS animations</title>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-composite-order">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes animLeft {
   to { left: 100px }
 }
 @keyframes animTop {
   to { top: 100px }
 }
 @keyframes animBottom {
@@ -23,24 +25,24 @@
   content: ''
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
+test(t => {
   assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
     + ' with no animations');
 }, 'getAnimations for non-animated content');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   // Add an animation
   div.style.animation = 'animLeft 100s';
   assert_equals(document.getAnimations().length, 1,
                 'getAnimations returns a running CSS Animation');
 
   // Add another animation
   div.style.animation = 'animLeft 100s, animTop 100s';
@@ -48,41 +50,41 @@ test(function(t) {
                 'getAnimations returns two running CSS Animations');
 
   // Remove both
   div.style.animation = '';
   assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Animations');
 }, 'getAnimations for CSS Animations');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
                         'animBottom 100s';
 
-  var animations = document.getAnimations();
+  const animations = document.getAnimations();
   assert_equals(animations.length, 4,
                 'getAnimations returns all running CSS Animations');
   assert_equals(animations[0].animationName, 'animLeft',
                 'Order of first animation returned');
   assert_equals(animations[1].animationName, 'animTop',
                 'Order of second animation returned');
   assert_equals(animations[2].animationName, 'animRight',
                 'Order of third animation returned');
   assert_equals(animations[3].animationName, 'animBottom',
                 'Order of fourth animation returned');
 }, 'Order of CSS Animations - within an element');
 
-test(function(t) {
-  var div1 = addDiv(t, { style: 'animation: animLeft 100s' });
-  var div2 = addDiv(t, { style: 'animation: animLeft 100s' });
-  var div3 = addDiv(t, { style: 'animation: animLeft 100s' });
-  var div4 = addDiv(t, { style: 'animation: animLeft 100s' });
+test(t => {
+  const div1 = addDiv(t, { style: 'animation: animLeft 100s' });
+  const div2 = addDiv(t, { style: 'animation: animLeft 100s' });
+  const div3 = addDiv(t, { style: 'animation: animLeft 100s' });
+  const div4 = addDiv(t, { style: 'animation: animLeft 100s' });
 
-  var animations = document.getAnimations();
+  let animations = document.getAnimations();
   assert_equals(animations.length, 4,
                 'getAnimations returns all running CSS Animations');
   assert_equals(animations[0].effect.target, div1,
                 'Order of first animation returned');
   assert_equals(animations[1].effect.target, div2,
                 'Order of second animation returned');
   assert_equals(animations[2].effect.target, div3,
                 'Order of third animation returned');
@@ -107,27 +109,27 @@ test(function(t) {
                 'Order of second animation returned after tree surgery');
   assert_equals(animations[2].effect.target, div4,
                 'Order of third animation returned after tree surgery');
   assert_equals(animations[3].effect.target, div3,
                 'Order of fourth animation returned after tree surgery');
 
 }, 'Order of CSS Animations - across elements');
 
-test(function(t) {
-  var div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
-  var div2 = addDiv(t, { style: 'animation: animBottom 100s' });
+test(t => {
+  const div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
+  const div2 = addDiv(t, { style: 'animation: animBottom 100s' });
 
-  var expectedResults = [ [ div1, 'animLeft' ],
-                          [ div1, 'animTop' ],
-                          [ div2, 'animBottom' ] ];
-  var animations = document.getAnimations();
+  let expectedResults = [ [ div1, 'animLeft' ],
+                            [ div1, 'animTop' ],
+                            [ div2, 'animBottom' ] ];
+  let animations = document.getAnimations();
   assert_equals(animations.length, expectedResults.length,
                 'getAnimations returns all running CSS Animations');
-  animations.forEach(function(anim, i) {
+  animations.forEach((anim, i) => {
     assert_equals(anim.effect.target, expectedResults[i][0],
                   'Target of animation in position ' + i);
     assert_equals(anim.animationName, expectedResults[i][1],
                   'Name of animation in position ' + i);
   });
 
   // Modify tree structure and animation list
   div2.appendChild(div1);
@@ -136,141 +138,141 @@ test(function(t) {
   expectedResults = [ [ div2, 'animBottom' ],
                       [ div1, 'animLeft' ],
                       [ div1, 'animRight' ],
                       [ div1, 'animTop' ] ];
   animations = document.getAnimations();
   assert_equals(animations.length, expectedResults.length,
                 'getAnimations returns all running CSS Animations after ' +
                 'making changes');
-  animations.forEach(function(anim, i) {
+  animations.forEach((anim, i) => {
     assert_equals(anim.effect.target, expectedResults[i][0],
                   'Target of animation in position ' + i + ' after changes');
     assert_equals(anim.animationName, expectedResults[i][1],
                   'Name of animation in position ' + i + ' after changes');
   });
 }, 'Order of CSS Animations - across and within elements');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
-  var animLeft = document.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
+  const animLeft = document.getAnimations()[0];
   assert_equals(animLeft.animationName, 'animLeft',
                 'Originally, animLeft animation comes first');
 
   // Disassociate animLeft from markup and restart
   div.style.animation = 'animTop 100s';
   animLeft.play();
 
-  var animations = document.getAnimations();
+  const animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'getAnimations returns markup-bound and free animations');
   assert_equals(animations[0].animationName, 'animTop',
                 'Markup-bound animations come first');
   assert_equals(animations[1], animLeft, 'Free animations come last');
 }, 'Order of CSS Animations - markup-bound vs free animations');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
-  var animLeft = document.getAnimations()[0];
-  var animTop  = document.getAnimations()[1];
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
+  const animLeft = document.getAnimations()[0];
+  const animTop  = document.getAnimations()[1];
 
   // Disassociate both animations from markup and restart in opposite order
   div.style.animation = '';
   animTop.play();
   animLeft.play();
 
-  var animations = document.getAnimations();
+  const animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'getAnimations returns free animations');
   assert_equals(animations[0], animTop,
                 'Free animations are returned in the order they are started');
   assert_equals(animations[1], animLeft,
                 'Animations started later are returned later');
 
   // Restarting an animation should have no effect
   animTop.cancel();
   animTop.play();
   assert_equals(document.getAnimations()[0], animTop,
                 'After restarting, the ordering of free animations' +
                 ' does not change');
 }, 'Order of CSS Animations - free animations');
 
-test(function(t) {
+test(t => {
   // Add an animation first
-  var div = addDiv(t, { style: 'animation: animLeft 100s' });
+  const div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.style.top = '0px';
   div.style.transition = 'all 100s';
   flushComputedStyle(div);
 
   // *Then* add a transition
   div.style.top = '100px';
   flushComputedStyle(div);
 
   // Although the transition was added later, it should come first in the list
-  var animations = document.getAnimations();
+  const animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'Both CSS animations and transitions are returned');
   assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
   assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second');
 }, 'Order of CSS Animations and CSS Transitions');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
   div.getAnimations()[0].finish();
   assert_equals(document.getAnimations().length, 1,
                 'Forwards-filling CSS animations are returned');
 }, 'Finished but filling CSS Animations are returned');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s' });
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.getAnimations()[0].finish();
   assert_equals(document.getAnimations().length, 0,
                 'Non-filling finished CSS animations are not returned');
 }, 'Finished but not filling CSS Animations are not returned');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
   assert_equals(document.getAnimations().length, 1,
                 'Yet-to-start CSS animations are returned');
 }, 'Yet-to-start CSS Animations are returned');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s' });
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.getAnimations()[0].cancel();
   assert_equals(document.getAnimations().length, 0,
-                'CSS animations cancelled by the API are not returned');
-}, 'CSS Animations cancelled via the API are not returned');
+                'CSS animations canceled by the API are not returned');
+}, 'CSS Animations canceled via the API are not returned');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: animLeft 100s' });
-  var anim = div.getAnimations()[0];
+test(t => {
+  const div = addDiv(t, { style: 'animation: animLeft 100s' });
+  const anim = div.getAnimations()[0];
   anim.cancel();
   anim.play();
   assert_equals(document.getAnimations().length, 1,
-                'CSS animations cancelled and restarted by the API are ' +
+                'CSS animations canceled and restarted by the API are ' +
                 'returned');
-}, 'CSS Animations cancelled and restarted via the API are returned');
+}, 'CSS Animations canceled and restarted via the API are returned');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
                 '#parent::before': 'animation: animRight 10s;' });
   // create two divs with these arrangement:
   //       parent
   //     ::before,
   //     ::after
   //        |
   //       child
-  var parent = addDiv(t, { 'id': 'parent' });
-  var child = addDiv(t);
+  const parent = addDiv(t, { 'id': 'parent' });
+  const child = addDiv(t);
   parent.appendChild(child);
-  [parent, child].forEach((div) => {
+  for (const div of [parent, child]) {
     div.setAttribute('style', 'animation: animBottom 10s');
-  });
+  }
 
-  var anims = document.getAnimations();
+  const anims = document.getAnimations();
   assert_equals(anims.length, 4,
                 'CSS animations on both pseudo-elements and elements ' +
                 'are returned');
   assert_equals(anims[0].effect.target, parent,
                 'The animation targeting the parent element comes first');
   assert_equals(anims[1].effect.target.type, '::before',
                 'The animation targeting the ::before element comes second');
   assert_equals(anims[2].effect.target.type, '::after',
rename from dom/animation/test/css-animations/test_animations-dynamic-changes.html
rename to testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html
--- a/dom/animation/test/css-animations/test_animations-dynamic-changes.html
+++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html
@@ -1,158 +1,165 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>
+Element.getAnimations() - Dynamic changes to the list of CSS animations
+</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim1 {
   to { left: 100px }
 }
 @keyframes anim2 { }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s';
-  var originalAnimation = div.getAnimations()[0];
-  var originalStartTime;
-  var originalCurrentTime;
+  const originalAnimation = div.getAnimations()[0];
 
   // Wait a moment so we can confirm the startTime doesn't change (and doesn't
   // simply reflect the current time).
-  return originalAnimation.ready.then(function() {
-    originalStartTime = originalAnimation.startTime;
-    originalCurrentTime = originalAnimation.currentTime;
+  await originalAnimation.ready;
+
+  const originalStartTime = originalAnimation.startTime;
+  const originalCurrentTime = originalAnimation.currentTime;
+
+  // Wait a moment so we can confirm the startTime doesn't change (and
+  // doesn't simply reflect the current time).
+  await waitForNextFrame();
 
-    // Wait a moment so we can confirm the startTime doesn't change (and
-    // doesn't simply reflect the current time).
-    return waitForNextFrame();
-  }).then(function() {
-    div.style.animationDuration = '200s';
-    var animation = div.getAnimations()[0];
-    assert_equals(animation, originalAnimation,
-                  'The same Animation is returned after updating'
-                  + ' animation duration');
-    assert_equals(animation.startTime, originalStartTime,
-                  'Animations returned by getAnimations preserve'
-                  + ' their startTime even when they are updated');
-    // Sanity check
-    assert_not_equals(animation.currentTime, originalCurrentTime,
-                      'Animation.currentTime has updated in next'
-                      + ' requestAnimationFrame callback');
-  });
+  div.style.animationDuration = '200s';
+  const animation = div.getAnimations()[0];
+  assert_equals(animation, originalAnimation,
+                'The same Animation is returned after updating'
+                + ' animation duration');
+  assert_equals(animation.startTime, originalStartTime,
+                'Animations returned by getAnimations preserve'
+                + ' their startTime even when they are updated');
+  // Sanity check
+  assert_not_equals(animation.currentTime, originalCurrentTime,
+                    'Animation.currentTime has updated in next'
+                    + ' requestAnimationFrame callback');
 }, 'Animations preserve their startTime when changed');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
 
   // Store original state
-  var animations = div.getAnimations();
-  var animation1 = animations[0];
-  var animation2 = animations[1];
+  let animations = div.getAnimations();
+  const animation1 = animations[0];
+  const animation2 = animations[1];
 
   // Update first in list
   div.style.animationDuration = '200s, 100s';
   animations = div.getAnimations();
   assert_equals(animations[0], animation1,
                 'First Animation is in same position after update');
   assert_equals(animations[1], animation2,
                 'Second Animation is in same position after update');
 }, 'Updated Animations maintain their order in the list');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 200s, anim1 100s';
 
   // Store original state
-  var animations = div.getAnimations();
-  var animation1 = animations[0];
-  var animation2 = animations[1];
+  let animations = div.getAnimations();
+  const animation1 = animations[0];
+  const animation2 = animations[1];
 
   // Wait before continuing so we can compare start times (otherwise the
   // new Animation objects and existing Animation objects will all have the same
   // start time).
-  return waitForAllAnimations(animations).then(waitForFrame).then(function() {
-    // Swap duration of first and second in list and prepend animation at the
-    // same time
-    div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
-    animations = div.getAnimations();
-    assert_true(animations[0] !== animation1 && animations[0] !== animation2,
-                'New Animation is prepended to start of list');
-    assert_equals(animations[1], animation1,
-                  'First Animation is in second position after update');
-    assert_equals(animations[2], animation2,
-                  'Second Animation is in third position after update');
-    assert_equals(animations[1].startTime, animations[2].startTime,
-                  'Old Animations have the same start time');
-    // TODO: Check that animations[0].startTime === null
-    return animations[0].ready;
-  }).then(function() {
-    assert_greater_than(animations[0].startTime, animations[1].startTime,
-                        'New Animation has later start time');
-  });
+  await waitForAllAnimations(animations);
+  await waitForFrame();
+
+  // Swap duration of first and second in list and prepend animation at the
+  // same time
+  div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
+  animations = div.getAnimations();
+  assert_true(animations[0] !== animation1 && animations[0] !== animation2,
+              'New Animation is prepended to start of list');
+  assert_equals(animations[1], animation1,
+                'First animation is in second position after update');
+  assert_equals(animations[2], animation2,
+                'Second animation is in third position after update');
+  assert_equals(animations[1].startTime, animations[2].startTime,
+                'Old animations have the same start time');
+  assert_equals(animations[0].startTime, null,
+                'New animation has a null start time');
+
+  await animations[0].ready;
+
+  assert_greater_than(animations[0].startTime, animations[1].startTime,
+                      'New animation has later start time');
 }, 'Only the startTimes of existing animations are preserved');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
-  var secondAnimation = div.getAnimations()[1];
+  const secondAnimation = div.getAnimations()[1];
 
   // Wait before continuing so we can compare start times
-  return secondAnimation.ready.then(waitForNextFrame).then(function() {
-    // Trim list of animations
-    div.style.animationName = 'anim1';
-    var animations = div.getAnimations();
-    assert_equals(animations.length, 1, 'List of Animations was trimmed');
-    assert_equals(animations[0], secondAnimation,
-                  'Remaining Animation is the second one in the list');
-    assert_equals(typeof(animations[0].startTime), 'number',
-                  'Remaining Animation has resolved startTime');
-    assert_less_than(animations[0].startTime,
-                     animations[0].timeline.currentTime,
-                     'Remaining Animation preserves startTime');
-  });
+  await secondAnimation.ready;
+  await waitForNextFrame();
+
+  // Trim list of animations
+  div.style.animationName = 'anim1';
+  const animations = div.getAnimations();
+  assert_equals(animations.length, 1, 'List of Animations was trimmed');
+  assert_equals(animations[0], secondAnimation,
+                'Remaining Animation is the second one in the list');
+  assert_equals(typeof(animations[0].startTime), 'number',
+                'Remaining Animation has resolved startTime');
+  assert_less_than(animations[0].startTime,
+                   animations[0].timeline.currentTime,
+                   'Remaining Animation preserves startTime');
 }, 'Animations are removed from the start of the list while preserving'
    + ' the state of existing Animations');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s';
-  var firstAddedAnimation = div.getAnimations()[0],
-      secondAddedAnimation,
-      animations;
+  const firstAddedAnimation = div.getAnimations()[0];
 
   // Wait and add second Animation
-  return firstAddedAnimation.ready.then(waitForFrame).then(function() {
-    div.style.animation = 'anim1 100s, anim1 100s';
-    secondAddedAnimation = div.getAnimations()[0];
+  await firstAddedAnimation.ready;
+  await waitForFrame();
+
+  div.style.animation = 'anim1 100s, anim1 100s';
+  const secondAddedAnimation = div.getAnimations()[0];
+
+  // Wait again and add another Animation
+  await secondAddedAnimation.ready;
+  await waitForFrame();
 
-    // Wait again and add another Animation
-    return secondAddedAnimation.ready.then(waitForFrame);
-  }).then(function() {
-    div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
-    animations = div.getAnimations();
-    assert_not_equals(firstAddedAnimation, secondAddedAnimation,
-                      'New Animations are added to start of the list');
-    assert_equals(animations[0], secondAddedAnimation,
-                  'Second Animation remains in same position after'
-                  + ' interleaving');
-    assert_equals(animations[2], firstAddedAnimation,
-                  'First Animation remains in same position after'
-                  + ' interleaving');
-    return animations[1].ready;
-  }).then(function() {
-    assert_greater_than(animations[1].startTime, animations[0].startTime,
-                        'Interleaved animation starts later than existing ' +
-                        'animations');
-    assert_greater_than(animations[0].startTime, animations[2].startTime,
-                        'Original animations retain their start time');
-  });
+  div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
+  const animations = div.getAnimations();
+  assert_not_equals(firstAddedAnimation, secondAddedAnimation,
+                    'New Animations are added to start of the list');
+  assert_equals(animations[0], secondAddedAnimation,
+                'Second Animation remains in same position after'
+                + ' interleaving');
+  assert_equals(animations[2], firstAddedAnimation,
+                'First Animation remains in same position after'
+                + ' interleaving');
+  await animations[1].ready;
+
+  assert_greater_than(animations[1].startTime, animations[0].startTime,
+                      'Interleaved animation starts later than existing ' +
+                      'animations');
+  assert_greater_than(animations[0].startTime, animations[2].startTime,
+                      'Original animations retain their start time');
 }, 'Animation state is preserved when interleaving animations in list');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_element-get-animations.html
rename to testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html
--- a/dom/animation/test/css-animations/test_element-get-animations.html
+++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html
@@ -1,13 +1,16 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Element.getAnimations() for CSS animations</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim1 {
   to { left: 100px }
 }
 @keyframes anim2 {
   to { top: 100px }
 }
 @keyframes multiPropAnim {
@@ -21,362 +24,353 @@
 }
 @keyframes empty { }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   assert_equals(div.getAnimations().length, 0,
     'getAnimations returns an empty sequence for an element'
     + ' with no animations');
 }, 'getAnimations for non-animated content');
 
-promise_test(function(t) {
-  var div = addDiv(t);
-
-  // FIXME: This test does too many things. It should be split up.
+promise_test(async t => {
+  const div = addDiv(t);
 
   // Add an animation
   div.style.animation = 'anim1 100s';
-  var animations = div.getAnimations();
+  let animations = div.getAnimations();
   assert_equals(animations.length, 1,
     'getAnimations returns an Animation running CSS Animations');
-  return animations[0].ready.then(function() {
-    var startTime = animations[0].startTime;
-    assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
-      'CSS animation has a sensible start time');
+  await animations[0].ready;
 
-    // Wait a moment then add a second animation.
-    //
-    // We wait for the next frame so that we can test that the start times of
-    // the animations differ.
-    return waitForFrame();
-  }).then(function() {
-    div.style.animation = 'anim1 100s, anim2 100s';
-    animations = div.getAnimations();
-    assert_equals(animations.length, 2,
-      'getAnimations returns one Animation for each value of'
-      + ' animation-name');
-    // Wait until both Animations are ready
-    // (We don't make any assumptions about the order of the Animations since
-    //  that is the purpose of the following test.)
-    return waitForAllAnimations(animations);
-  }).then(function() {
-    assert_true(animations[0].startTime < animations[1].startTime,
-      'Additional Animations for CSS animations start after the original'
-      + ' animation and appear later in the list');
-  });
+  // Add a second animation
+  div.style.animation = 'anim1 100s, anim2 100s';
+  animations = div.getAnimations();
+  assert_equals(animations.length, 2,
+    'getAnimations returns one CSSAnimation for each value of animation-name');
+  // (We don't check the order of the Animations since that is covered by tests
+  //  later in this file.)
 }, 'getAnimations for CSS Animations');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'animation: anim1 100s' });
+test(t => {
+  const div = addDiv(t, { style: 'animation: anim1 100s' });
   assert_class_string(div.getAnimations()[0], 'CSSAnimation',
                       'Interface of returned animation is CSSAnimation');
 }, 'getAnimations returns CSSAnimation objects for CSS Animations');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   // Add an animation that targets multiple properties
   div.style.animation = 'multiPropAnim 100s';
   assert_equals(div.getAnimations().length, 1,
     'getAnimations returns only one Animation for a CSS Animation'
     + ' that targets multiple properties');
 }, 'getAnimations for multi-property animations');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
   // Add an animation
   div.style.backgroundColor = 'red';
   div.style.animation = 'anim1 100s';
   getComputedStyle(div).backgroundColor;
 
   // Wait until a frame after the animation starts, then add a transition
-  var animations = div.getAnimations();
-  return animations[0].ready.then(waitForFrame).then(function() {
-    div.style.transition = 'all 100s';
-    div.style.backgroundColor = 'green';
+  let animations = div.getAnimations();
+  await animations[0].ready;
+  await waitForFrame();
+
+  div.style.transition = 'all 100s';
+  div.style.backgroundColor = 'green';
 
-    animations = div.getAnimations();
-    assert_equals(animations.length, 2,
-      'getAnimations returns Animations for both animations and'
-      + ' transitions that run simultaneously');
-    assert_class_string(animations[0], 'CSSTransition',
-                        'First-returned animation is the CSS Transition');
-    assert_class_string(animations[1], 'CSSAnimation',
-                        'Second-returned animation is the CSS Animation');
-  });
+  animations = div.getAnimations();
+  assert_equals(animations.length, 2,
+    'getAnimations returns Animations for both animations and'
+    + ' transitions that run simultaneously');
+  assert_class_string(animations[0], 'CSSTransition',
+                      'First-returned animation is the CSS Transition');
+  assert_class_string(animations[1], 'CSSAnimation',
+                      'Second-returned animation is the CSS Animation');
 }, 'getAnimations for both CSS Animations and CSS Transitions at once');
 
-async_test(function(t) {
-  var div = addDiv(t);
+async_test(t => {
+  const div = addDiv(t);
 
   // Set up event listener
-  div.addEventListener('animationend', t.step_func(function() {
+  div.addEventListener('animationend', t.step_func(() => {
     assert_equals(div.getAnimations().length, 0,
       'getAnimations does not return Animations for finished '
       + ' (and non-forwards-filling) CSS Animations');
     t.done();
   }));
 
   // Add a very short animation
   div.style.animation = 'anim1 0.01s';
 }, 'getAnimations for CSS Animations that have finished');
 
-async_test(function(t) {
-  var div = addDiv(t);
+async_test(t => {
+  const div = addDiv(t);
 
   // Set up event listener
-  div.addEventListener('animationend', t.step_func(function() {
+  div.addEventListener('animationend', t.step_func(() => {
     assert_equals(div.getAnimations().length, 1,
       'getAnimations returns Animations for CSS Animations that have'
       + ' finished but are filling forwards');
     t.done();
   }));
 
   // Add a very short animation
   div.style.animation = 'anim1 0.01s forwards';
 }, 'getAnimations for CSS Animations that have finished but are'
    + ' forwards filling');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'none 100s';
 
-  var animations = div.getAnimations();
+  let animations = div.getAnimations();
   assert_equals(animations.length, 0,
     'getAnimations returns an empty sequence for an element'
     + ' with animation-name: none');
 
   div.style.animation = 'none 100s, anim1 100s';
   animations = div.getAnimations();
   assert_equals(animations.length, 1,
     'getAnimations returns Animations only for those CSS Animations whose'
     + ' animation-name is not none');
 }, 'getAnimations for CSS Animations with animation-name: none');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'missing 100s';
-  var animations = div.getAnimations();
+  let animations = div.getAnimations();
   assert_equals(animations.length, 0,
     'getAnimations returns an empty sequence for an element'
     + ' with animation-name: missing');
 
   div.style.animation = 'anim1 100s, missing 100s';
   animations = div.getAnimations();
   assert_equals(animations.length, 1,
     'getAnimations returns Animations only for those CSS Animations whose'
     + ' animation-name is found');
 }, 'getAnimations for CSS Animations with animation-name: missing');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s, notyet 100s';
-  var animations = div.getAnimations();
+  let animations = div.getAnimations();
   assert_equals(animations.length, 1,
     'getAnimations initally only returns Animations for CSS Animations whose'
     + ' animation-name is found');
 
-  return animations[0].ready.then(waitForFrame).then(function() {
-    var keyframes = '@keyframes notyet { to { left: 100px; } }';
-    document.styleSheets[0].insertRule(keyframes, 0);
-    animations = div.getAnimations();
-    assert_equals(animations.length, 2,
-      'getAnimations includes Animation when @keyframes rule is added'
-      + ' later');
-    return waitForAllAnimations(animations);
-  }).then(function() {
-    assert_true(animations[0].startTime < animations[1].startTime,
-      'Newly added animation has a later start time');
-    document.styleSheets[0].deleteRule(0);
-  });
+  await animations[0].ready;
+  await waitForFrame();
+
+  const keyframes = '@keyframes notyet { to { left: 100px; } }';
+  document.styleSheets[0].insertRule(keyframes, 0);
+  animations = div.getAnimations();
+  assert_equals(animations.length, 2,
+    'getAnimations includes Animation when @keyframes rule is added'
+    + ' later');
+  await waitForAllAnimations(animations);
+
+  assert_true(animations[0].startTime < animations[1].startTime,
+    'Newly added animation has a later start time');
+  document.styleSheets[0].deleteRule(0);
 }, 'getAnimations for CSS Animations where the @keyframes rule is added'
    + ' later');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s, anim1 100s';
   assert_equals(div.getAnimations().length, 2,
     'getAnimations returns one Animation for each CSS animation-name'
     + ' even if the names are duplicated');
 }, 'getAnimations for CSS Animations with duplicated animation-name');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'empty 100s';
   assert_equals(div.getAnimations().length, 1,
     'getAnimations returns Animations for CSS animations with an'
     + ' empty keyframes rule');
 }, 'getAnimations for CSS Animations with empty keyframes rule');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s 100s';
-  var animations = div.getAnimations();
+  const animations = div.getAnimations();
   assert_equals(animations.length, 1,
     'getAnimations returns animations for CSS animations whose'
     + ' delay makes them start later');
-  return animations[0].ready.then(waitForFrame).then(function() {
-    assert_true(animations[0].startTime <= document.timeline.currentTime,
-      'For CSS Animations in delay phase, the start time of the Animation is'
-      + ' not in the future');
-  });
+  await animations[0].ready;
+  await waitForFrame();
+
+  assert_true(animations[0].startTime <= document.timeline.currentTime,
+    'For CSS Animations in delay phase, the start time of the Animation is'
+    + ' not in the future');
 }, 'getAnimations for CSS animations in delay phase');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 0s 100s';
   assert_equals(div.getAnimations().length, 1,
     'getAnimations returns animations for CSS animations whose'
     + ' duration is zero');
   div.remove();
 }, 'getAnimations for zero-duration CSS Animations');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s';
-  var originalAnimation = div.getAnimations()[0];
+  const originalAnimation = div.getAnimations()[0];
 
   // Update pause state (an Animation change)
   div.style.animationPlayState = 'paused';
-  var pendingAnimation = div.getAnimations()[0];
+  const pendingAnimation = div.getAnimations()[0];
   assert_equals(pendingAnimation.playState, 'paused',
                 'animation\'s play state is updated');
   assert_equals(originalAnimation, pendingAnimation,
                 'getAnimations returns the same objects even when their'
                 + ' play state changes');
 
   // Update duration (an Animation change)
   div.style.animationDuration = '200s';
-  var extendedAnimation = div.getAnimations()[0];
-  // FIXME: Check extendedAnimation.effect.timing.duration has changed once the
-  // API is available
+  const extendedAnimation = div.getAnimations()[0];
+  assert_equals(
+    extendedAnimation.effect.getTiming().duration,
+    200 * MS_PER_SEC,
+    'animation\'s duration has been updated'
+  );
   assert_equals(originalAnimation, extendedAnimation,
                 'getAnimations returns the same objects even when their'
                 + ' duration changes');
 }, 'getAnimations returns objects with the same identity');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim1 100s';
 
   assert_equals(div.getAnimations().length, 1,
-    'getAnimations returns an animation before cancelling');
+    'getAnimations returns an animation before canceling');
 
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
 
   animation.cancel();
   assert_equals(div.getAnimations().length, 0,
-    'getAnimations does not return cancelled animations');
+    'getAnimations does not return canceled animations');
 
   animation.play();
   assert_equals(div.getAnimations().length, 1,
-    'getAnimations returns cancelled animations that have been re-started');
+    'getAnimations returns canceled animations that have been re-started');
 
-}, 'getAnimations for CSS Animations that are cancelled');
+}, 'getAnimations for CSS Animations that are canceled');
 
-promise_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.animation = 'anim2 100s';
 
-  return div.getAnimations()[0].ready.then(function() {
-    // Prepend to the list and test that even though anim1 was triggered
-    // *after* anim2, it should come first because it appears first
-    // in the animation-name property.
-    div.style.animation = 'anim1 100s, anim2 100s';
-    var anims = div.getAnimations();
-    assert_equals(anims[0].animationName, 'anim1',
-                  'animation order after prepending to list');
-    assert_equals(anims[1].animationName, 'anim2',
-                  'animation order after prepending to list');
+  await div.getAnimations()[0].ready;
+
+  // Prepend to the list and test that even though anim1 was triggered
+  // *after* anim2, it should come first because it appears first
+  // in the animation-name property.
+  div.style.animation = 'anim1 100s, anim2 100s';
+  let anims = div.getAnimations();
+  assert_equals(anims[0].animationName, 'anim1',
+                'animation order after prepending to list');
+  assert_equals(anims[1].animationName, 'anim2',
+                'animation order after prepending to list');
 
-    // Normally calling cancel and play would this push anim1 to the top of
-    // the stack but it shouldn't for CSS animations that map an the
-    // animation-name property.
-    var anim1 = anims[0];
-    anim1.cancel();
-    anim1.play();
-    anims = div.getAnimations();
-    assert_equals(anims[0].animationName, 'anim1',
-                  'animation order after cancelling and restarting');
-    assert_equals(anims[1].animationName, 'anim2',
-                  'animation order after cancelling and restarting');
-  });
+  // Normally calling cancel and play would this push anim1 to the top of
+  // the stack but it shouldn't for CSS animations that map an the
+  // animation-name property.
+  const anim1 = anims[0];
+  anim1.cancel();
+  anim1.play();
+  anims = div.getAnimations();
+  assert_equals(anims[0].animationName, 'anim1',
+                'animation order after canceling and restarting');
+  assert_equals(anims[1].animationName, 'anim2',
+                'animation order after canceling and restarting');
 }, 'getAnimations for CSS Animations follows animation-name order');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '#target::after': 'animation: anim1 10s;',
                 '#target::before': 'animation: anim1 10s;' });
-  var target = addDiv(t, { 'id': 'target' });
+  const target = addDiv(t, { 'id': 'target' });
   target.style.animation = 'anim1 100s';
 
-  var animations = target.getAnimations({ subtree: false });
+  const animations = target.getAnimations({ subtree: false });
   assert_equals(animations.length, 1,
                 'Should find only the element');
   assert_equals(animations[0].effect.target, target,
                 'Effect target should be the element');
-}, 'Test AnimationFilter{ subtree: false } with single element');
+}, '{ subtree: false } on a leaf element returns the element\'s animations'
+   + ' and ignore pseudo-elements');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '#target::after': 'animation: anim1 10s;',
                 '#target::before': 'animation: anim1 10s;' });
-  var target = addDiv(t, { 'id': 'target' });
+  const target = addDiv(t, { 'id': 'target' });
   target.style.animation = 'anim1 100s';
 
-  var animations = target.getAnimations({ subtree: true });
+  const animations = target.getAnimations({ subtree: true });
   assert_equals(animations.length, 3,
                 'getAnimations({ subtree: true }) ' +
                 'should return animations on pseudo-elements');
   assert_equals(animations[0].effect.target, target,
                 'The animation targeting the parent element ' +
                 'should be returned first');
   assert_equals(animations[1].effect.target.type, '::before',
                 'The animation targeting the ::before pseudo-element ' +
                 'should be returned second');
   assert_equals(animations[2].effect.target.type, '::after',
                 'The animation targeting the ::after pesudo-element ' +
                 'should be returned last');
-}, 'Test AnimationFilter{ subtree: true } with single element');
+}, '{ subtree: true } on a leaf element returns the element\'s animations'
+   + ' and its pseudo-elements\' animations');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '#parent::after': 'animation: anim1 10s;',
                 '#parent::before': 'animation: anim1 10s;',
                 '#child::after': 'animation: anim1 10s;',
                 '#child::before': 'animation: anim1 10s;' });
-  var parent = addDiv(t, { 'id': 'parent' });
+  const parent = addDiv(t, { 'id': 'parent' });
   parent.style.animation = 'anim1 100s';
-  var child = addDiv(t, { 'id': 'child' });
+  const child = addDiv(t, { 'id': 'child' });
   child.style.animation = 'anim1 100s';
   parent.appendChild(child);
 
-  var animations = parent.getAnimations({ subtree: false });
+  const animations = parent.getAnimations({ subtree: false });
   assert_equals(animations.length, 1,
                 'Should find only the element even if it has a child');
   assert_equals(animations[0].effect.target, parent,
                 'Effect target shuld be the element');
-}, 'Test AnimationFilter{ subtree: false } with element that has a child');
+}, '{ subtree: false } on an element with a child returns only the element\'s'
+   + ' animations');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '#parent::after': 'animation: anim1 10s;',
                 '#parent::before': 'animation: anim1 10s;',
                 '#child::after': 'animation: anim1 10s;',
                 '#child::before': 'animation: anim1 10s;' });
-  var parent = addDiv(t, { 'id': 'parent' });
-  var child = addDiv(t, { 'id': 'child' });
+  const parent = addDiv(t, { 'id': 'parent' });
+  const child = addDiv(t, { 'id': 'child' });
   parent.style.animation = 'anim1 100s';
   child.style.animation = 'anim1 100s';
   parent.appendChild(child);
 
-  var animations = parent.getAnimations({ subtree: true });
+  const animations = parent.getAnimations({ subtree: true });
   assert_equals(animations.length, 6,
                 'Should find all elements, pesudo-elements that parent has');
 
   assert_equals(animations[0].effect.target, parent,
                 'The animation targeting the parent element ' +
                 'should be returned first');
   assert_equals(animations[1].effect.target.type, '::before',
                 'The animation targeting the ::before pseudo-element ' +
@@ -397,37 +391,38 @@ test(function(t) {
                 'should be returned fifth');
   assert_equals(animations[4].effect.target.parentElement, child,
                 'This ::before element should be child of child element');
   assert_equals(animations[5].effect.target.type, '::after',
                 'The animation targeting the ::after pesudo-element ' +
                 'should be returned last');
   assert_equals(animations[5].effect.target.parentElement, child,
                 'This ::after element should be child of child element');
-}, 'Test AnimationFilter{ subtree: true } with element that has a child');
+}, '{ subtree: true } on an element with a child returns animations from the'
+   + ' element, its pseudo-elements, its child and its child pseudo-elements');
 
-test(function(t) {
-  var parent = addDiv(t, { 'id': 'parent' });
-  var child1 = addDiv(t, { 'id': 'child1' });
-  var grandchild1 = addDiv(t, { 'id': 'grandchild1' });
-  var grandchild2 = addDiv(t, { 'id': 'grandchild2' });
-  var child2 = addDiv(t, { 'id': 'child2' });
+test(t => {
+  const parent = addDiv(t, { 'id': 'parent' });
+  const child1 = addDiv(t, { 'id': 'child1' });
+  const grandchild1 = addDiv(t, { 'id': 'grandchild1' });
+  const grandchild2 = addDiv(t, { 'id': 'grandchild2' });
+  const child2 = addDiv(t, { 'id': 'child2' });
 
   parent.style.animation = 'anim1 100s';
   child1.style.animation = 'anim1 100s';
   grandchild1.style.animation = 'anim1 100s';
   grandchild2.style.animation = 'anim1 100s';
   child2.style.animation = 'anim1 100s';
 
   parent.appendChild(child1);
   child1.appendChild(grandchild1);
   child1.appendChild(grandchild2);
   parent.appendChild(child2);
 
-  var animations = parent.getAnimations({ subtree: true });
+  const animations = parent.getAnimations({ subtree: true });
   assert_equals(
     parent.getAnimations({ subtree: true }).length, 5,
                          'Should find all descendants of the element');
 
   assert_equals(animations[0].effect.target, parent,
                 'The animation targeting the parent element ' +
                 'should be returned first');
 
@@ -442,12 +437,13 @@ test(function(t) {
   assert_equals(animations[3].effect.target, grandchild2,
                 'The animation targeting the grandchild2 element ' +
                 'should be returned fourth');
 
   assert_equals(animations[4].effect.target, child2,
                 'The animation targeting the child2 element ' +
                 'should be returned last');
 
-}, 'Test AnimationFilter{ subtree: true } with element that has many descendant');
+}, '{ subtree: true } on an element with many descendants returns animations'
+   + ' from all the descendants');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_keyframeeffect-getkeyframes.html
rename to testing/web-platform/tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html
--- a/dom/animation/test/css-animations/test_keyframeeffect-getkeyframes.html
+++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html
@@ -1,13 +1,16 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>KeyframeEffect.getKeyframes() for CSS animations</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim-empty { }
 
 @keyframes anim-empty-frames {
   from { }
   to   { }
 }
 
@@ -154,33 +157,31 @@
   to { --not-used: 200px }
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 "use strict";
 
-function getKeyframes(e) {
-  return e.getAnimations()[0].effect.getKeyframes();
-}
+const getKeyframes = elem => elem.getAnimations()[0].effect.getKeyframes();
 
-function assert_frames_equal(a, b, name) {
+const assert_frames_equal = (a, b, name) => {
   assert_equals(Object.keys(a).sort().toString(),
                 Object.keys(b).sort().toString(),
                 "properties on " + name);
-  for (var p in a) {
+  for (const p in a) {
     if (p === 'offset' || p === 'computedOffset') {
       assert_approx_equals(a[p], b[p], 0.00001,
                            "value for '" + p + "' on " + name);
     } else {
       assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
     }
   }
-}
+};
 
 // animation-timing-function values to test with, where the value
 // is exactly the same as its serialization, sorted by the order
 // getKeyframes() will group frames with the same easing function
 // together (by nsTimingFunction::Compare).
 const kTimingFunctionValues = [
   "ease",
   "linear",
@@ -190,18 +191,18 @@ const kTimingFunctionValues = [
   "steps(1, start)",
   "steps(2, start)",
   "steps(1)",
   "steps(2)",
   "cubic-bezier(0, 0, 1, 1)",
   "cubic-bezier(0, 0.25, 0.75, 1)"
 ];
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-empty 100s';
   assert_equals(getKeyframes(div).length, 0,
                 "number of frames with empty @keyframes");
 
   div.style.animation = 'anim-empty-frames 100s';
   assert_equals(getKeyframes(div).length, 0,
                 "number of frames when @keyframes has empty keyframes");
@@ -213,544 +214,544 @@ test(function(t) {
 
   div.style.animation = 'anim-only-non-animatable 100s';
   assert_equals(getKeyframes(div).length, 0,
                 "number of frames when @keyframes only has frames with " +
                 "non-animatable properties");
 }, 'KeyframeEffect.getKeyframes() returns no frames for various kinds'
    + ' of empty enimations');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-simple 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease",
       color: "rgb(0, 0, 0)", composite: null },
     { offset: 1, computedOffset: 1, easing: "ease",
       color: "rgb(255, 255, 255)", composite: null },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
    + ' animation');
 
-test(function(t) {
-  kTimingFunctionValues.forEach(function(easing) {
-    var div = addDiv(t);
+test(t => {
+  for (const easing of kTimingFunctionValues) {
+    const div = addDiv(t);
 
     div.style.animation = 'anim-simple-three 100s ' + easing;
-    var frames = getKeyframes(div);
+    const frames = getKeyframes(div);
 
     assert_equals(frames.length, 3, "number of frames");
 
-    for (var i = 0; i < frames.length; i++) {
+    for (let i = 0; i < frames.length; i++) {
       assert_equals(frames[i].easing, easing,
                     "value for 'easing' on ComputedKeyframe #" + i);
     }
-  });
+  }
 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
    + ' values, when the easing comes from animation-timing-function on the'
    + ' element');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-simple-timing 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 3, "number of frames");
   assert_equals(frames[0].easing, "linear",
                 "value of 'easing' on ComputedKeyframe #0");
   assert_equals(frames[1].easing, "ease-in-out",
                 "value of 'easing' on ComputedKeyframe #1");
   assert_equals(frames[2].easing, "steps(1)",
                 "value of 'easing' on ComputedKeyframe #2");
 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
    + ' values, when the easing is specified on each keyframe');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-simple-timing-some 100s step-start';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 3, "number of frames");
   assert_equals(frames[0].easing, "linear",
                 "value of 'easing' on ComputedKeyframe #0");
   assert_equals(frames[1].easing, "steps(1, start)",
                 "value of 'easing' on ComputedKeyframe #1");
   assert_equals(frames[2].easing, "steps(1, start)",
                 "value of 'easing' on ComputedKeyframe #2");
 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
    + ' values, when the easing is specified on some keyframes');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-simple-shorthand 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       marginBottom: "8px", marginLeft: "8px",
       marginRight: "8px", marginTop: "8px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       marginBottom: "16px", marginLeft: "16px",
       marginRight: "16px", marginTop: "16px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
    + ' animation that specifies a single shorthand property');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-omit-to 100s';
   div.style.color = 'rgb(255, 255, 255)';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       color: "rgb(0, 0, 255)" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with a 0% keyframe and no 100% keyframe');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-omit-from 100s';
   div.style.color = 'rgb(255, 255, 255)';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(0, 0, 255)" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with a 100% keyframe and no 0% keyframe');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-omit-from-to 100s';
   div.style.color = 'rgb(255, 255, 255)';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 3, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0,   computedOffset: 0,   easing: "ease", composite: null,
       color: "rgb(255, 255, 255)" },
     { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: null,
       color: "rgb(0, 0, 255)" },
     { offset: 1,   computedOffset: 1,   easing: "ease", composite: null,
       color: "rgb(255, 255, 255)" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with no 0% or 100% keyframe but with a 50% keyframe');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-partially-omit-to 100s';
   div.style.marginTop = '250px';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       marginTop: '50px', marginBottom: '100px' },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       marginTop: '250px', marginBottom: '200px' },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with a partially complete 100% keyframe (because the ' +
    '!important rule is ignored)');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-different-props 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 4, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       color: "rgb(0, 0, 0)", marginTop: "8px" },
     { offset: 0.25, computedOffset: 0.25, easing: "ease", composite: null,
       color: "rgb(0, 0, 255)" },
     { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: null,
       marginTop: "12px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)", marginTop: "16px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with different properties on different keyframes, all ' +
    'with the same easing function');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-different-props-and-easing 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 4, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "linear", composite: null,
       color: "rgb(0, 0, 0)", marginTop: "8px" },
     { offset: 0.25, computedOffset: 0.25, easing: "steps(1)", composite: null,
       color: "rgb(0, 0, 255)" },
     { offset: 0.75, computedOffset: 0.75, easing: "ease-in", composite: null,
       marginTop: "12px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)", marginTop: "16px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with different properties on different keyframes, with ' +
    'a different easing function on each');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-merge-offset 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       color: "rgb(0, 0, 0)", marginTop: "8px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)", marginTop: "16px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with multiple keyframes for the same time, and all with ' +
    'the same easing function');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-merge-offset-and-easing 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 3, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "steps(1)", composite: null,
       color: "rgb(0, 0, 0)", fontSize: "16px" },
     { offset: 0, computedOffset: 0, easing: "linear", composite: null,
       marginTop: "8px", paddingLeft: "2px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px",
       paddingLeft: "4px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with multiple keyframes for the same time and with ' +
    'different easing functions');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-no-merge-equiv-easing 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 3, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "steps(1)", composite: null,
       marginTop: "0px", marginRight: "0px", marginBottom: "0px" },
     { offset: 0.5, computedOffset: 0.5, easing: "steps(1)", composite: null,
       marginTop: "10px", marginRight: "10px", marginBottom: "10px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       marginTop: "20px", marginRight: "20px", marginBottom: "20px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
    'animation with multiple keyframes for the same time and with ' +
    'different but equivalent easing functions');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-overriding 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 6, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       paddingTop: "30px" },
     { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: null,
       paddingTop: "20px" },
     { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: null,
       paddingTop: "20px" },
     { offset: 0.85, computedOffset: 0.85, easing: "ease", composite: null,
       paddingTop: "30px" },
     { offset: 0.851, computedOffset: 0.851, easing: "ease", composite: null,
       paddingTop: "60px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       paddingTop: "70px" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected frames for ' +
    'overlapping keyframes');
 
 // Gecko-specific test case: We are specifically concerned here that the
 // computed value for filter, "none", is correctly represented.
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-filter 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       filter: "none" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       filter: "blur(5px) sepia(60%) saturate(30%)" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with filter properties and missing keyframes');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-filter-drop-shadow 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       filter: "drop-shadow(rgb(0, 255, 0) 10px 10px 10px)" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       filter: "drop-shadow(rgb(255, 0, 0) 50px 30px 10px)" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animation with drop-shadow of filter property');
 
 // Gecko-specific test case: We are specifically concerned here that the
 // computed value for text-shadow and a "none" specified on a keyframe
 // are correctly represented.
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.textShadow = '1px 1px 2px rgb(0, 0, 0), ' +
                          '0 0 16px rgb(0, 0, 255), ' +
                          '0 0 3.2px rgb(0, 0, 255)';
   div.style.animation = 'anim-text-shadow 100s';
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       textShadow: "rgb(0, 0, 0) 1px 1px 2px,"
                   + " rgb(0, 0, 255) 0px 0px 16px,"
                   + " rgb(0, 0, 255) 0px 0px 3.2px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       textShadow: "none" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with text-shadow properties and missing keyframes');
 
 // Gecko-specific test case: We are specifically concerned here that the
 // initial value for background-size and the specified list are correctly
 // represented.
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.animation = 'anim-background-size 100s';
-  var frames = getKeyframes(div);
+  let frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       backgroundSize: "auto auto" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       backgroundSize: "50% auto, 6px auto, contain" },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 
   // Test inheriting a background-size value
 
   expected[0].backgroundSize = div.style.backgroundSize =
     "30px auto, 40% auto, auto auto";
   frames = getKeyframes(div);
 
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i
                         + " after updating current style");
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with background-size properties and missing keyframes');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim-variables 100s';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       transform: "none" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       transform: "translate(100px, 0px)" },
   ];
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with CSS variables as keyframe values');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim-variables-shorthand 100s';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       marginBottom: "0px",
       marginLeft: "0px",
       marginRight: "0px",
       marginTop: "0px" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       marginBottom: "100px",
       marginLeft: "100px",
       marginRight: "100px",
       marginTop: "100px" },
   ];
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with CSS variables as keyframe values in a shorthand property');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim-custom-property-in-keyframe 100s';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       color: "rgb(0, 0, 0)" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       color: "rgb(0, 255, 0)" },
   ];
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with a CSS variable which is overriden by the value in keyframe');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim-only-custom-property-in-keyframe 100s';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   assert_equals(frames.length, 2, "number of frames");
 
-  var expected = [
+  const expected = [
     { offset: 0, computedOffset: 0, easing: "ease", composite: null,
       transform: "translate(100px, 0px)" },
     { offset: 1, computedOffset: 1, easing: "ease", composite: null,
       transform: "none" },
   ];
-  for (var i = 0; i < frames.length; i++) {
+  for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
   }
 }, 'KeyframeEffect.getKeyframes() returns expected values for ' +
    'animations with only custom property in a keyframe');
 
 </script>
 </body>
rename from dom/animation/test/css-animations/test_effect-target.html
rename to testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html
--- a/dom/animation/test/css-animations/test_effect-target.html
+++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html
@@ -1,55 +1,58 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSAnimation.effect.target</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
 @keyframes anim { }
 ::before {
   content: ''
 }
 ::after {
   content: ''
 }
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.animation = 'anim 100s';
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(animation.effect.target, div,
     'Animation.target is the animatable div');
 }, 'Returned CSS animations have the correct effect target');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
-  var div = addDiv(t, { class: 'after' });
-  var anims = document.getAnimations();
+  const div = addDiv(t, { class: 'after' });
+  const anims = document.getAnimations();
   assert_equals(anims.length, 2,
                 'Got animations running on ::after pseudo element');
   assert_equals(anims[0].effect.target, anims[1].effect.target,
                 'Both animations return the same target object');
 }, 'effect.target should return the same CSSPseudoElement object each time');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '.after::after': 'animation: anim 10s;' });
-  var div = addDiv(t, { class: 'after' });
-  var pseudoTarget = document.getAnimations()[0].effect.target;
-  var effect = new KeyframeEffect(pseudoTarget,
-                                  { background: ["blue", "red"] },
-                                  3 * MS_PER_SEC);
-  var newAnim = new Animation(effect, document.timeline);
+  const div = addDiv(t, { class: 'after' });
+  const pseudoTarget = document.getAnimations()[0].effect.target;
+  const effect = new KeyframeEffect(pseudoTarget,
+                                    { background: ["blue", "red"] },
+                                    3 * MS_PER_SEC);
+  const newAnim = new Animation(effect, document.timeline);
   newAnim.play();
-  var anims = document.getAnimations();
+  const anims = document.getAnimations();
   assert_equals(anims.length, 2,
                 'Got animations running on ::after pseudo element');
   assert_not_equals(anims[0], newAnim,
                     'The scriped-generated animation appears last');
   assert_equals(newAnim.effect.target, pseudoTarget,
                 'The effect.target of the scripted-generated animation is ' +
                 'the same as the one from the argument of ' +
                 'KeyframeEffect constructor');
rename from dom/animation/test/css-animations/test_event-dispatch.html
rename to testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html
--- a/dom/animation/test/css-animations/test_event-dispatch.html
+++ b/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html
@@ -1,380 +1,423 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>Tests for CSS animation event dispatch</title>
 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
-  @keyframes anim {
-    from { margin-left: 0px; }
-    to { margin-left: 100px; }
-  }
+@keyframes anim {
+  from { margin-left: 0px; }
+  to { margin-left: 100px; }
+}
 </style>
 <body>
 <div id="log"></div>
 <script>
 'use strict';
 
-/**
- * Helper class to record the elapsedTime member of each event.
- * The EventWatcher class in testharness.js allows us to wait on
- * multiple events in a certain order but only records the event
- * parameters of the most recent event.
- */
-function AnimationEventHandler(target) {
-  this.target = target;
-  this.target.onanimationstart = evt => {
-   this.animationstart = evt.elapsedTime;
-  };
-  this.target.onanimationiteration = evt => {
-    this.animationiteration = evt.elapsedTime;
-  };
-  this.target.onanimationend = evt => {
-    this.animationend = evt.elapsedTime;
-  };
-  this.target.onanimationcancel = evt => {
-    this.animationcancel = evt.elapsedTime;
-  };
-}
-AnimationEventHandler.prototype.clear = () => {
-  this.animationstart     = undefined;
-  this.animationiteration = undefined;
-  this.animationend       = undefined;
-  this.animationcancel    = undefined;
-}
-
-function setupAnimation(t, animationStyle) {
+const setupAnimation = (t, animationStyle) => {
   const div = addDiv(t, { style: 'animation: ' + animationStyle });
-  // Note that this AnimationEventHandler should be created before EventWatcher
-  // to capture all events in the handler prior to the EventWatcher since
-  // testharness.js proceeds when the EventWatcher received watching events.
-  const handler = new AnimationEventHandler(div);
   const watcher = new EventWatcher(t, div, [ 'animationstart',
-                                           'animationiteration',
-                                           'animationend',
-                                           'animationcancel' ]);
+                                             'animationiteration',
+                                             'animationend',
+                                             'animationcancel' ]);
   const animation = div.getAnimations()[0];
 
-  return { animation, watcher, div, handler };
-}
+  return { animation, watcher, div };
+};
 
-promise_test(t => {
+promise_test(async t => {
   // Add 1ms delay to ensure that the delay is not included in the elapsedTime.
   const { animation, watcher } = setupAnimation(t, 'anim 100s 1ms');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  const evt = await watcher.wait_for('animationstart');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Idle -> Active');
 
-promise_test(t => {
-  const { animation, watcher, div, handler } = setupAnimation(t, 'anim 100s');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+
+  // Seek to After phase.
+  animation.finish();
+
+  const events = await watcher.wait_for(['animationstart', 'animationend'], {
+    record: 'all',
+  });
+  assert_equals(events[0].elapsedTime, 0.0);
+  assert_equals(events[1].elapsedTime, 100);
+}, 'Idle -> After');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
+
+  await animation.ready;
+
+  // Seek to Active phase.
+  animation.currentTime = 100 * MS_PER_SEC;
+
+  const evt = await watcher.wait_for('animationstart');
+  assert_equals(evt.elapsedTime, 0.0);
+}, 'Before -> Active');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
+  const allEvents = watcher.wait_for(['animationstart', 'animationend'], {
+    record: 'all',
+  });
+
+  await animation.ready;
 
   // Seek to After phase.
   animation.finish();
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(() => {
-    assert_equals(handler.animationstart, 0.0);
-    assert_equals(handler.animationend, 100);
-  });
-}, 'Idle -> After');
 
-promise_test(t => {
-  const { animation, watcher } =
-    setupAnimation(t, 'anim 100s 100s paused');
-
-  return animation.ready.then(() => {
-    // Seek to Active phase.
-    animation.currentTime = 100 * MS_PER_SEC;
-    return watcher.wait_for('animationstart');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
-}, 'Before -> Active');
-
-promise_test(t => {
-  const { animation, watcher, div, handler } =
-    setupAnimation(t, 'anim 100s 100s paused');
-
-  return animation.ready.then(() => {
-    // Seek to After phase.
-    animation.finish();
-    return watcher.wait_for([ 'animationstart', 'animationend' ]);
-  }).then(evt => {
-    assert_equals(handler.animationstart, 0.0);
-    assert_equals(handler.animationend, 100.0);
-  });
+  const events = await allEvents;
+  assert_equals(events[0].elapsedTime, 0.0);
+  assert_equals(events[1].elapsedTime, 100.0);
 }, 'Before -> After');
 
-promise_test(t => {
+promise_test(async t => {
   const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  div.style.display = 'none';
+
+  const evt = await watcher.wait_for('animationcancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, display: none');
 
-promise_test(t => {
-  const { animation, watcher, div } = setupAnimation(t, 'anim 100s');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+
+  await watcher.wait_for('animationstart');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    animation.currentTime = 100.0;
-    // Make idle
-    animation.timeline = null;
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    assert_time_equals_literal(evt.elapsedTime, 0.1);
-  });
+  animation.currentTime = 100.0;
+
+  // Make idle
+  animation.timeline = null;
+
+  const evt = await watcher.wait_for('animationcancel');
+  assert_time_equals_literal(evt.elapsedTime, 0.1);
 }, 'Active -> Idle, setting Animation.timeline = null');
 
-promise_test(t => {
-  // we should NOT pause animation since calling cancel synchronously.
-  const { animation, watcher, div } = setupAnimation(t, 'anim 100s');
+promise_test(async t => {
+  // We should NOT pause animation since calling cancel synchronously.
+  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+
+  await watcher.wait_for('animationstart');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    animation.currentTime = 50.0;
-    animation.cancel();
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    assert_time_equals_literal(evt.elapsedTime, 0.05);
-  });
+  animation.currentTime = 50.0;
+  animation.cancel();
+
+  const evt = await watcher.wait_for('animationcancel');
+  assert_time_equals_literal(evt.elapsedTime, 0.05);
 }, 'Active -> Idle, calling Animation.cancel()');
 
-promise_test(t => {
-  const { animation, watcher } =
-    setupAnimation(t, 'anim 100s 100s paused');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
 
   // Seek to Active phase.
   animation.currentTime = 100 * MS_PER_SEC;
-  return watcher.wait_for('animationstart').then(() => {
-    // Seek to Before phase.
-    animation.currentTime = 0;
-    return watcher.wait_for('animationend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for('animationstart');
+
+  // Seek to Before phase.
+  animation.currentTime = 0;
+
+  const evt = await watcher.wait_for('animationend');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Before');
 
-promise_test(t => {
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s paused');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Seek to After phase.
-    animation.finish();
-    return watcher.wait_for('animationend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100.0);
-  });
+  await watcher.wait_for('animationstart');
+
+  // Seek to After phase.
+  animation.finish();
+
+  const evt = await watcher.wait_for('animationend');
+  assert_equals(evt.elapsedTime, 100.0);
 }, 'Active -> After');
 
-promise_test(t => {
-  const { animation, watcher, div, handler } =
-    setupAnimation(t, 'anim 100s 100s paused');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
 
   // Seek to After phase.
   animation.finish();
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(() => {
-    // Seek to Before phase.
-    animation.currentTime = 0;
-    handler.clear();
-    return watcher.wait_for([ 'animationstart', 'animationend' ]);
-  }).then(() => {
-    assert_equals(handler.animationstart, 100.0);
-    assert_equals(handler.animationend, 0.0);
+  await watcher.wait_for([ 'animationstart', 'animationend' ]);
+
+  // Seek to Before phase.
+  animation.currentTime = 0;
+
+  const events = await watcher.wait_for(['animationstart', 'animationend'], {
+    record: 'all',
   });
+  assert_equals(events[0].elapsedTime, 100.0);
+  assert_equals(events[1].elapsedTime, 0.0);
 }, 'After -> Before');
 
-promise_test(t => {
-  const { animation, watcher, div } =
-    setupAnimation(t, 'anim 100s 100s paused');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
 
   // Seek to After phase.
   animation.finish();
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(() => {
-    // Seek to Active phase.
-    animation.currentTime = 100 * MS_PER_SEC;
-    return watcher.wait_for('animationstart');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100.0);
-  });
+  await watcher.wait_for([ 'animationstart', 'animationend' ]);
+
+  // Seek to Active phase.
+  animation.currentTime = 100 * MS_PER_SEC;
+
+  const evt = await watcher.wait_for('animationstart');
+  assert_equals(evt.elapsedTime, 100.0);
 }, 'After -> Active');
 
-promise_test(t => {
-  const { animation, watcher, div }
-    = setupAnimation(t, 'anim 100s 100s 3 paused');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3 paused');
+
+  await animation.ready;
+
+  // Seek to iteration 0 (no animationiteration event should be dispatched)
+  animation.currentTime = 100 * MS_PER_SEC;
+  await watcher.wait_for('animationstart');
 
-  return animation.ready.then(() => {
-    // Seek to iteration 0 (no animationiteration event should be dispatched)
-    animation.currentTime = 100 * MS_PER_SEC;
-    return watcher.wait_for('animationstart');
-  }).then(evt => {
-    // Seek to iteration 2
-    animation.currentTime = 300 * MS_PER_SEC;
-    return watcher.wait_for('animationiteration');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 200);
-    // Seek to After phase (no animationiteration event should be dispatched)
-    animation.currentTime = 400 * MS_PER_SEC;
-    return watcher.wait_for('animationend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 300);
-  });
+  // Seek to iteration 2
+  animation.currentTime = 300 * MS_PER_SEC;
+  let evt = await watcher.wait_for('animationiteration');
+  assert_equals(evt.elapsedTime, 200);
+
+  // Seek to After phase (no animationiteration event should be dispatched)
+  animation.currentTime = 400 * MS_PER_SEC;
+  evt = await watcher.wait_for('animationend');
+  assert_equals(evt.elapsedTime, 300);
 }, 'Active -> Active (forwards)');
 
-promise_test(t => {
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3');
 
   // Seek to After phase.
   animation.finish();
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(() => {
-    // Seek to iteration 2 (no animationiteration event should be dispatched)
-    animation.pause();
-    animation.currentTime = 300 * MS_PER_SEC;
-    return watcher.wait_for('animationstart');
-  }).then(() => {
-    // Seek to mid of iteration 0 phase.
-    animation.currentTime = 200 * MS_PER_SEC;
-    return watcher.wait_for('animationiteration');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 200.0);
-    // Seek to before phase (no animationiteration event should be dispatched)
-    animation.currentTime = 0;
-    return watcher.wait_for('animationend');
-  });
+  await watcher.wait_for([ 'animationstart', 'animationend' ]);
+
+  // Seek to iteration 2 (no animationiteration event should be dispatched)
+  animation.pause();
+  animation.currentTime = 300 * MS_PER_SEC;
+  await watcher.wait_for('animationstart');
+
+  // Seek to mid of iteration 0 phase.
+  animation.currentTime = 200 * MS_PER_SEC;
+
+  const evt = await watcher.wait_for('animationiteration');
+  assert_equals(evt.elapsedTime, 200.0);
+
+  // Seek to before phase (no animationiteration event should be dispatched)
+  animation.currentTime = 0;
+  await watcher.wait_for('animationend');
 }, 'Active -> Active (backwards)');
 
-promise_test(t => {
-  const { animation, watcher, div } =
-    setupAnimation(t, 'anim 100s paused');
-  return watcher.wait_for('animationstart').then(evt => {
-    // Seek to Idle phase.
-    div.style.display = 'none';
-    flushComputedStyle(div);
+promise_test(async t => {
+  const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
+
+  await watcher.wait_for('animationstart');
 
-    return watcher.wait_for('animationcancel');
-  }).then(() => {
-    // Restart this animation.
-    div.style.display = '';
-    return watcher.wait_for('animationstart');
-  });
+  // Seek to Idle phase.
+  div.style.display = 'none';
+  flushComputedStyle(div);
+
+  await watcher.wait_for('animationcancel');
+
+  // Restart this animation.
+  div.style.display = '';
+  await watcher.wait_for('animationstart');
 }, 'Active -> Idle -> Active: animationstart is fired by restarting animation');
 
-promise_test(t => {
-  const { animation, watcher } =
-    setupAnimation(t, 'anim 100s 100s 2 paused');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 2 paused');
 
   // Make After.
   animation.finish();
-  return watcher.wait_for([ 'animationstart',
-                            'animationend' ]).then(evt => {
-    animation.playbackRate = -1;
-    return watcher.wait_for('animationstart');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 200);
-    // Seek to 1st iteration
-    animation.currentTime = 200 * MS_PER_SEC - 1;
-    return watcher.wait_for('animationiteration');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100);
-    // Seek to before
-    animation.currentTime = 100 * MS_PER_SEC - 1;
-    return watcher.wait_for('animationend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0);
-    assert_equals(animation.playState, 'running'); // delay
-  });
+
+  await watcher.wait_for([ 'animationstart', 'animationend' ]);
+  animation.playbackRate = -1;
+
+  let evt = await watcher.wait_for('animationstart');
+  assert_equals(evt.elapsedTime, 200);
+
+  // Seek to 1st iteration
+  animation.currentTime = 200 * MS_PER_SEC - 1;
+
+  evt = await watcher.wait_for('animationiteration');
+  assert_equals(evt.elapsedTime, 100);
+
+  // Seek to before
+  animation.currentTime = 100 * MS_PER_SEC - 1;
+
+  evt = await watcher.wait_for('animationend');
+  assert_equals(evt.elapsedTime, 0);
+  assert_equals(animation.playState, 'running'); // delay
 }, 'Negative playbackRate sanity test(Before -> Active -> Before)');
 
-promise_test(t => {
-  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  animation.currentTime = 150 * MS_PER_SEC;
+  animation.currentTime = 50 * MS_PER_SEC;
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, before -> active, then back');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  animation.currentTime = 250 * MS_PER_SEC;
+  animation.currentTime = 50 * MS_PER_SEC;
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, before -> after, then back');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    animation.cancel();
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    animation.cancel();
-    // Then wait a couple of frames and check that no event was dispatched.
-    return waitForAnimationFrames(2);
-  });
-}, 'Call Animation.cancel after cancelling animation.');
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  // Get us into the initial state:
+  animation.currentTime = 150 * MS_PER_SEC;
+
+  await watcher.wait_for('animationstart');
+
+  animation.currentTime = 50 * MS_PER_SEC;
+  animation.currentTime = 150 * MS_PER_SEC;
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, active -> before, then back');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  // Get us into the initial state:
+  animation.currentTime = 150 * MS_PER_SEC;
 
-promise_test(t => {
-  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+  await watcher.wait_for('animationstart');
+
+  animation.currentTime = 250 * MS_PER_SEC;
+  animation.currentTime = 150 * MS_PER_SEC;
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, active -> after, then back');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  // Get us into the initial state:
+  animation.currentTime = 250 * MS_PER_SEC;
+
+  await watcher.wait_for(['animationstart', 'animationend']);
+
+  animation.currentTime = 50 * MS_PER_SEC;
+  animation.currentTime = 250 * MS_PER_SEC;
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    animation.cancel();
-    animation.play();
-    return watcher.wait_for([ 'animationcancel',
-                              'animationstart' ]);
-  });
-}, 'Restart animation after cancelling animation immediately.');
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, after -> before, then back');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
+
+  // Get us into the initial state:
+  animation.currentTime = 250 * MS_PER_SEC;
 
-promise_test(t => {
+  await watcher.wait_for(['animationstart', 'animationend']);
+
+  animation.currentTime = 150 * MS_PER_SEC;
+  animation.currentTime = 250 * MS_PER_SEC;
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Redundant change, after -> active, then back');
+
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    animation.cancel();
-    animation.play();
-    animation.cancel();
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    // Then wait a couple of frames and check that no event was dispatched.
-    return waitForAnimationFrames(2);
-  });
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  animation.cancel();
+  await watcher.wait_for('animationcancel');
+
+  animation.cancel();
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Call Animation.cancel after canceling animation.');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  animation.cancel();
+  animation.play();
+  await watcher.wait_for([ 'animationcancel', 'animationstart' ]);
+}, 'Restart animation after canceling animation immediately.');
+
+promise_test(async t => {
+  const { animation, watcher } = setupAnimation(t, 'anim 100s');
+
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  animation.cancel();
+  animation.play();
+  animation.cancel();
+  await watcher.wait_for('animationcancel');
+
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
 }, 'Call Animation.cancel after restarting animation immediately.');
 
-promise_test(t => {
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    animation.timeline = null;
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    animation.timeline = document.timeline;
-    animation.play();
-    return watcher.wait_for('animationstart');
-  });
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  animation.timeline = null;
+  await watcher.wait_for('animationcancel');
+
+  animation.timeline = document.timeline;
+  animation.play();
+  await watcher.wait_for('animationstart');
 }, 'Set timeline and play transition after clearing the timeline.');
 
-promise_test(t => {
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    // Make idle
-    animation.cancel();
-    return watcher.wait_for('animationcancel');
-  }).then(evt => {
-    animation.effect = null;
-    // Then wait a couple of frames and check that no event was dispatched.
-    return waitForAnimationFrames(2);
-  });
-}, 'Set null target effect after cancelling the animation.');
+  await watcher.wait_for('animationstart');
+
+  // Make idle
+  animation.cancel();
+  await watcher.wait_for('animationcancel');
 
-promise_test(t => {
+  animation.effect = null;
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
+}, 'Set null target effect after canceling the animation.');
+
+promise_test(async t => {
   const { animation, watcher } = setupAnimation(t, 'anim 100s');
 
-  return watcher.wait_for('animationstart').then(evt => {
-    animation.effect = null;
-    return watcher.wait_for('animationend');
-  }).then(evt => {
-    animation.cancel();
-    // Then wait a couple of frames and check that no event was dispatched.
-    return waitForAnimationFrames(2);
-  });
+  await watcher.wait_for('animationstart');
+
+  animation.effect = null;
+  await watcher.wait_for('animationend');
+
+  animation.cancel();
+  // Then wait a couple of frames and check that no event was dispatched.
+  await waitForAnimationFrames(2);
 }, 'Cancel the animation after clearing the target effect.');
 
 </script>
 </body>
 </html>
rename from dom/animation/test/css-animations/test_event-order.html
rename to testing/web-platform/tests/css/css-animations/event-order.tentative.html
--- a/dom/animation/test/css-animations/test_event-order.html
+++ b/testing/web-platform/tests/css/css-animations/event-order.tentative.html
@@ -1,15 +1,15 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>Tests for CSS animation event order</title>
 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { margin-left: 0px; }
     to { margin-left: 100px; }
   }
 </style>
 <body>
 <div id="log"></div>
@@ -18,146 +18,147 @@
 
 /**
  * Asserts that the set of actual and received events match.
  * @param actualEvents   An array of the received AnimationEvent objects.
  * @param expectedEvents A series of array objects representing the expected
  *        events, each having the form:
  *          [ event type, target element, elapsed time ]
  */
-function checkEvents(actualEvents, ...expectedEvents) {
+const checkEvents = (actualEvents, ...expectedEvents) => {
   assert_equals(actualEvents.length, expectedEvents.length,
                 `Number of actual events (${actualEvents.length}: \
 ${actualEvents.map(event => event.type).join(', ')}) should match expected \
 events (${expectedEvents.map(event => event.type).join(', ')})`);
 
   actualEvents.forEach((actualEvent, i) => {
     assert_equals(expectedEvents[i][0], actualEvent.type,
                   'Event type should match');
     assert_equals(expectedEvents[i][1], actualEvent.target,
                   'Event target should match');
     assert_equals(expectedEvents[i][2], actualEvent.elapsedTime,
                   'Event\'s elapsed time should match');
   });
-}
+};
 
-function setupAnimation(t, animationStyle, receiveEvents) {
+const setupAnimation = (t, animationStyle, receiveEvents) => {
   const div = addDiv(t, { style: "animation: " + animationStyle });
 
-  ['start', 'iteration', 'end'].forEach(name => {
+  for (const name of ['start', 'iteration', 'end']) {
     div['onanimation' + name] = evt => {
     receiveEvents.push({ type:        evt.type,
                          target:      evt.target,
                          elapsedTime: evt.elapsedTime });
     };
-  });
+  }
 
   const watcher = new EventWatcher(t, div, [ 'animationstart',
                                              'animationiteration',
                                              'animationend' ]);
 
   const animation = div.getAnimations()[0];
 
   return [animation, watcher, div];
-}
+};
 
-promise_test(t => {
+promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 100s 2 paused', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 100s 2 paused', events);
 
-  return Promise.all([ watcher1.wait_for('animationstart'),
-                       watcher2.wait_for('animationstart') ]).then(() => {
-    checkEvents(events, ['animationstart', div1, 0],
-                        ['animationstart', div2, 0]);
+  await Promise.all([ watcher1.wait_for('animationstart'),
+                      watcher2.wait_for('animationstart') ]);
+
+  checkEvents(events, ['animationstart', div1, 0],
+                      ['animationstart', div2, 0]);
 
-    events.length = 0;  // Clear received event array
+  events.length = 0;  // Clear received event array
+
+  animation1.currentTime = 100 * MS_PER_SEC;
+  animation2.currentTime = 100 * MS_PER_SEC;
+
+  await Promise.all([ watcher1.wait_for('animationiteration'),
+                      watcher2.wait_for('animationiteration') ]);
 
-    animation1.currentTime = 100 * MS_PER_SEC;
-    animation2.currentTime = 100 * MS_PER_SEC;
-    return Promise.all([ watcher1.wait_for('animationiteration'),
-                         watcher2.wait_for('animationiteration') ]);
-  }).then(() => {
-    checkEvents(events, ['animationiteration', div1, 100],
-                        ['animationiteration', div2, 100]);
+  checkEvents(events, ['animationiteration', div1, 100],
+                      ['animationiteration', div2, 100]);
 
-    events.length = 0;  // Clear received event array
+  events.length = 0;  // Clear received event array
 
-    animation1.finish();
-    animation2.finish();
+  animation1.finish();
+  animation2.finish();
 
-    return Promise.all([ watcher1.wait_for('animationend'),
-                         watcher2.wait_for('animationend') ]);
-  }).then(() => {
-    checkEvents(events, ['animationend', div1, 200],
-                        ['animationend', div2, 200]);
-  });
+  await Promise.all([ watcher1.wait_for('animationend'),
+                      watcher2.wait_for('animationend') ]);
+
+  checkEvents(events, ['animationend', div1, 200],
+                      ['animationend', div2, 200]);
 }, 'Test same events are ordered by elements.');
 
-promise_test(t => {
+promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 200s 400s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 300s 2', events);
 
-  return watcher2.wait_for('animationstart').then(evt => {
-    animation1.currentTime = 400 * MS_PER_SEC;
-    animation2.currentTime = 400 * MS_PER_SEC;
+  await watcher2.wait_for('animationstart');
 
-    events.length = 0;  // Clear received event array
+  animation1.currentTime = 400 * MS_PER_SEC;
+  animation2.currentTime = 400 * MS_PER_SEC;
 
-    return Promise.all([ watcher1.wait_for('animationstart'),
-                         watcher2.wait_for('animationiteration') ]);
-  }).then(() => {
-    checkEvents(events, ['animationiteration', div2, 300],
-                        ['animationstart',     div1, 0]);
-  });
+  events.length = 0;  // Clear received event array
+
+  await Promise.all([ watcher1.wait_for('animationstart'),
+                      watcher2.wait_for('animationiteration') ]);
+
+  checkEvents(events, ['animationiteration', div2, 300],
+                      ['animationstart',     div1, 0]);
 }, 'Test start and iteration events are ordered by time.');
 
-promise_test(t => {
+promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 150s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 100s 2', events);
 
-  return Promise.all([ watcher1.wait_for('animationstart'),
-                       watcher2.wait_for('animationstart') ]).then(() => {
-    animation1.currentTime = 150 * MS_PER_SEC;
-    animation2.currentTime = 150 * MS_PER_SEC;
+  await Promise.all([ watcher1.wait_for('animationstart'),
+                      watcher2.wait_for('animationstart') ]);
 
-    events.length = 0;  // Clear received event array
+  animation1.currentTime = 150 * MS_PER_SEC;
+  animation2.currentTime = 150 * MS_PER_SEC;
 
-    return Promise.all([ watcher1.wait_for('animationend'),
-                         watcher2.wait_for('animationiteration') ]);
-  }).then(() => {
-    checkEvents(events, ['animationiteration', div2, 100],
-                        ['animationend',       div1, 150]);
-  });
+  events.length = 0;  // Clear received event array
+
+  await Promise.all([ watcher1.wait_for('animationend'),
+                      watcher2.wait_for('animationiteration') ]);
+
+  checkEvents(events, ['animationiteration', div2, 100],
+                      ['animationend',       div1, 150]);
 }, 'Test iteration and end events are ordered by time.');
 
-promise_test(t => {
+promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 100s 100s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 100s 2', events);
 
   animation1.finish();
   animation2.finish();
 
-  return Promise.all([ watcher1.wait_for([ 'animationstart',
-                                           'animationend' ]),
-                       watcher2.wait_for([ 'animationstart',
-                                           'animationend' ]) ]).then(() => {
-    checkEvents(events, ['animationstart', div2, 0],
-                        ['animationstart', div1, 0],
-                        ['animationend',   div1, 100],
-                        ['animationend',   div2, 200]);
-  });
+  await Promise.all([ watcher1.wait_for([ 'animationstart',
+                                          'animationend' ]),
+                      watcher2.wait_for([ 'animationstart',
+                                          'animationend' ]) ]);
+
+  checkEvents(events, ['animationstart', div2, 0],
+                      ['animationstart', div1, 0],
+                      ['animationend',   div1, 100],
+                      ['animationend',   div2, 200]);
 }, 'Test start and end events are sorted correctly when fired simultaneously');
 
 </script>
 </body>
 </html>
copy from dom/animation/test/testcommon.js
copy to testing/web-platform/tests/css/css-animations/support/testcommon.js
--- a/dom/animation/test/testcommon.js
+++ b/testing/web-platform/tests/css/css-animations/support/testcommon.js
@@ -31,117 +31,16 @@ function assert_times_equal(actual, expe
 
 /*
  * Compare a time value based on its precision requirements with a fixed value.
  */
 function assert_time_equals_literal(actual, expected, description) {
   assert_approx_equals(actual, expected, TIME_PRECISION, description);
 }
 
-/*
- * Compare matrix string like 'matrix(1, 0, 0, 1, 100, 0)'.
- * This function allows error, 0.01, because on Android when we are scaling down
- * the document, it results in some errors.
- */
-function assert_matrix_equals(actual, expected, description) {
-  var matrixRegExp = /^matrix\((.+),(.+),(.+),(.+),(.+),(.+)\)/;
-  assert_regexp_match(actual, matrixRegExp,
-    'Actual value should be a matrix')
-  assert_regexp_match(expected, matrixRegExp,
-    'Expected value should be a matrix');
-
-  var actualMatrixArray = actual.match(matrixRegExp).slice(1).map(Number);
-  var expectedMatrixArray = expected.match(matrixRegExp).slice(1).map(Number);
-
-  assert_equals(actualMatrixArray.length, expectedMatrixArray.length,
-    'Array lengths should be equal (got \'' + expected + '\' and \'' + actual +
-    '\'): ' + description);
-  for (var i = 0; i < actualMatrixArray.length; i++) {
-    assert_approx_equals(actualMatrixArray[i], expectedMatrixArray[i], 0.01,
-      'Matrix array should be equal (got \'' + expected + '\' and \'' + actual +
-      '\'): ' + description);
-  }
-}
-
-/**
- * Compare given values which are same format of
- * KeyframeEffectReadonly::GetProperties.
- */
-function assert_properties_equal(actual, expected) {
-  assert_equals(actual.length, expected.length);
-
-  const compareProperties = (a, b) =>
-    a.property == b.property ? 0 : (a.property < b.property ? -1 : 1);
-
-  const sortedActual   = actual.sort(compareProperties);
-  const sortedExpected = expected.sort(compareProperties);
-
-  const serializeValues = values =>
-    values.map(value =>
-      '{ ' +
-          [ 'offset', 'value', 'easing', 'composite' ].map(
-            member => `${member}: ${value[member]}`
-          ).join(', ') +
-                      ' }')
-          .join(', ');
-
-  for (let i = 0; i < sortedActual.length; i++) {
-    assert_equals(sortedActual[i].property,
-                  sortedExpected[i].property,
-                  'CSS property name should match');
-    assert_equals(serializeValues(sortedActual[i].values),
-                  serializeValues(sortedExpected[i].values),
-                  `Values arrays do not match for `
-                  + `${sortedActual[i].property} property`);
-  }
-}
-
-/**
- * Construct a object which is same to a value of
- * KeyframeEffectReadonly::GetProperties().
- * The method returns undefined as a value in case of missing keyframe.
- * Therefor, we can use undefined for |value| and |easing| parameter.
- * @param offset - keyframe offset. e.g. 0.1
- * @param value - any keyframe value. e.g. undefined '1px', 'center', 0.5
- * @param composite - 'replace', 'add', 'accumulate'
- * @param easing - e.g. undefined, 'linear', 'ease' and so on
- * @return Object -
- *   e.g. { offset: 0.1, value: '1px', composite: 'replace', easing: 'ease'}
- */
-function valueFormat(offset, value, composite, easing) {
-  return { offset: offset, value: value, easing: easing, composite: composite };
-}
-
-/**
- * Appends a div to the document body and creates an animation on the div.
- * NOTE: This function asserts when trying to create animations with durations
- * shorter than 100s because the shorter duration may cause intermittent
- * failures.  If you are not sure how long it is suitable, use 100s; it's
- * long enough but shorter than our test framework timeout (330s).
- * If you really need to use shorter durations, use animate() function directly.
- *
- * @param t  The testharness.js Test object. If provided, this will be used
- *           to register a cleanup callback to remove the div when the test
- *           finishes.
- * @param attrs  A dictionary object with attribute names and values to set on
- *               the div.
- * @param frames  The keyframes passed to Element.animate().
- * @param options  The options passed to Element.animate().
- */
-function addDivAndAnimate(t, attrs, frames, options) {
-  let animDur = (typeof options === 'object') ?
-    options.duration : options;
-  assert_greater_than_equal(animDur, 100 * MS_PER_SEC,
-      'Clients of this addDivAndAnimate API must request a duration ' +
-      'of at least 100s, to avoid intermittent failures from e.g.' +
-      'the main thread being busy for an extended period');
-
-  return addDiv(t, attrs).animate(frames, options);
-}
-
 /**
  * Appends a div to the document body.
  *
  * @param t  The testharness.js Test object. If provided, this will be used
  *           to register a cleanup callback to remove the div when the test
  *           finishes.
  *
  * @param attrs  A dictionary object with attribute names and values to set on
@@ -189,32 +88,16 @@ function addStyle(t, rules) {
   if (t && typeof t.add_cleanup === 'function') {
     t.add_cleanup(function() {
       extraStyle.remove();
     });
   }
 }
 
 /**
- * Takes a CSS property (e.g. margin-left) and returns the equivalent IDL
- * name (e.g. marginLeft).
- */
-function propertyToIDL(property) {
-  var prefixMatch = property.match(/^-(\w+)-/);
-  if (prefixMatch) {
-    var prefix = prefixMatch[1] === 'moz' ? 'Moz' : prefixMatch[1];
-    property = prefix + property.substring(prefixMatch[0].length - 1);
-  }
-  // https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
-  return property.replace(/-([a-z])/gi, function(str, group) {
-    return group.toUpperCase();
-  });
-}
-
-/**
  * Promise wrapper for requestAnimationFrame.
  */
 function waitForFrame() {
   return new Promise(function(resolve, reject) {
     window.requestAnimationFrame(resolve);
   });
 }
 
@@ -257,177 +140,34 @@ function waitForAnimationFrames(frameCou
         window.requestAnimationFrame(handleFrame); // wait another frame
       }
     }
     window.requestAnimationFrame(handleFrame);
   });
 }
 
 /**
- * Promise wrapper for requestIdleCallback.
- */
-function waitForIdle() {
-  return new Promise(resolve => {
-    requestIdleCallback(resolve);
-  });
-}
-
-/**
  * Wrapper that takes a sequence of N animations and returns:
  *
  *   Promise.all([animations[0].ready, animations[1].ready, ... animations[N-1].ready]);
  */
 function waitForAllAnimations(animations) {
-  return Promise.all(animations.map(function(animation) {
-    return animation.ready;
-  }));
+  return Promise.all(animations.map(animation => animation.ready));
 }
 
 /**
  * Flush the computed style for the given element. This is useful, for example,
  * when we are testing a transition and need the initial value of a property
  * to be computed so that when we synchronouslyet set it to a different value
  * we actually get a transition instead of that being the initial value.
  */
 function flushComputedStyle(elem) {
   var cs = getComputedStyle(elem);
   cs.marginLeft;
 }
-
-if (opener) {
-  for (var funcName of ["async_test", "assert_not_equals", "assert_equals",
-                        "assert_approx_equals", "assert_less_than",
-                        "assert_less_than_equal", "assert_greater_than",
-                        "assert_between_inclusive",
-                        "assert_true", "assert_false",
-                        "assert_class_string", "assert_throws",
-                        "assert_unreached", "assert_regexp_match",
-                        "promise_test", "test"]) {
-    if (opener[funcName]) {
-      window[funcName] = opener[funcName].bind(opener);
-    }
-  }
-
-  window.EventWatcher = opener.EventWatcher;
-
-  function done() {
-    opener.add_completion_callback(function() {
-      self.close();
-    });
-    opener.done();
-  }
-}
-
-/*
- * Returns a promise that is resolved when the document has finished loading.
- */
-function waitForDocumentLoad() {
-  return new Promise(function(resolve, reject) {
-    if (document.readyState === "complete") {
-      resolve();
-    } else {
-      window.addEventListener("load", resolve);
-    }
-  });
-}
-
-/*
- * Enters test refresh mode, and restores the mode when |t| finishes.
- */
-function useTestRefreshMode(t) {
-  function ensureNoSuppressedPaints() {
-    return new Promise(resolve => {
-      function checkSuppressedPaints() {
-        if (!SpecialPowers.DOMWindowUtils.paintingSuppressed) {
-          resolve();
-        } else {
-          window.requestAnimationFrame(checkSuppressedPaints);
-        }
-      }
-      checkSuppressedPaints();
-    });
-  }
-
-  return ensureNoSuppressedPaints().then(() => {
-    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
-    t.add_cleanup(() => {
-      SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
-    });
-  });
-}
-
-/**
- * Returns true if off-main-thread animations.
- */
-function isOMTAEnabled() {
-  const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
-  return SpecialPowers.DOMWindowUtils.layerManagerRemote &&
-         SpecialPowers.getBoolPref(OMTAPrefKey);
-}
-
-/**
- * Append an SVG element to the target element.
- *
- * @param target The element which want to append.
- * @param attrs  A array object with attribute name and values to set on
- *               the SVG element.
- * @return An SVG outer element.
- */
-function addSVGElement(target, tag, attrs) {
-  if (!target) {
-    return null;
-  }
-  var element = document.createElementNS('http://www.w3.org/2000/svg', tag);
-  if (attrs) {
-    for (var attrName in attrs) {
-      element.setAttributeNS