Merge mozilla-inbound to mozilla-central. a=merge
authorDorel Luca <dluca@mozilla.com>
Sun, 19 Aug 2018 00:36:16 +0300
changeset 487387 6190326e1b38
parent 487346 1d20a072ee18 (current diff)
parent 487386 a13ee04709ba (diff)
child 487388 eef8c5edb73c
child 487389 0061cf77f918
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
6190326e1b38 / 63.0a1 / 20180818220049 / files
nightly linux64
6190326e1b38 / 63.0a1 / 20180818220049 / files
nightly mac
6190326e1b38 / 63.0a1 / 20180818220049 / files
nightly win32
6190326e1b38 / 63.0a1 / 20180818220049 / files
nightly win64
6190326e1b38 / 63.0a1 / 20180818220049 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
dom/animation/test/css-transitions/test_animation-cancel.html
dom/animation/test/css-transitions/test_animation-computed-timing.html
dom/animation/test/css-transitions/test_animation-currenttime.html
dom/animation/test/css-transitions/test_animation-finished.html
dom/animation/test/css-transitions/test_animation-pausing.html
dom/animation/test/css-transitions/test_animation-ready.html
dom/animation/test/css-transitions/test_animation-starttime.html
dom/animation/test/css-transitions/test_csstransition-transitionproperty.html
dom/animation/test/css-transitions/test_document-get-animations.html
dom/animation/test/css-transitions/test_effect-target.html
dom/animation/test/css-transitions/test_element-get-animations.html
dom/animation/test/css-transitions/test_event-dispatch.html
dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html
dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
dom/animation/test/css-transitions/test_setting-effect.html
testing/web-platform/tests/css/css-animations/CSSAnimation-getComputedTiming.tentative.html
toolkit/themes/linux/global/icons/Close.gif
toolkit/themes/linux/global/icons/Minimize.gif
toolkit/themes/linux/global/icons/Restore.gif
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1801,17 +1801,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rsdparsa"
 version = "0.1.0"
 dependencies = [
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
 ]
 
 [[package]]
 name = "rsdparsa_capi"
 version = "0.1.0"
 dependencies = [
@@ -2036,17 +2036,17 @@ dependencies = [
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "new-ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
  "num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "servo_arc 0.1.1",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3243,16 +3243,18 @@ function BrowserReloadWithFlags(reloadFl
   // Unfortunately, we'll count the remoteness flip case as a
   // "newURL" load, since we're using loadURI, but hopefully
   // that's rare enough to not matter.
   maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "reload");
 
   // Reset temporary permissions on the current tab. This is done here
   // because we only want to reset permissions on user reload.
   SitePermissions.clearTemporaryPermissions(gBrowser.selectedBrowser);
+  PanelMultiView.hidePopup(gIdentityHandler._identityPopup);
+
 
   let handlingUserInput = window.windowUtils.isHandlingUserInput;
 
   gBrowser.selectedBrowser
           .messageManager
           .sendAsyncMessage("Browser:Reload",
                             { flags: reloadFlags, handlingUserInput });
 }
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -3348,16 +3348,17 @@ window._gBrowser = {
     }
   },
 
   reloadTab(aTab) {
     let browser = this.getBrowserForTab(aTab);
     // Reset temporary permissions on the current tab. This is done here
     // because we only want to reset permissions on user reload.
     SitePermissions.clearTemporaryPermissions(browser);
+    PanelMultiView.hidePopup(gIdentityHandler._identityPopup);
     browser.reload();
   },
 
   addProgressListener(aListener) {
     if (arguments.length != 1) {
       Cu.reportError("gBrowser.addProgressListener was " +
         "called with a second argument, " +
         "which is not supported. See bug " +
--- a/browser/base/content/test/permissions/browser_permissions.js
+++ b/browser/base/content/test/permissions/browser_permissions.js
@@ -261,8 +261,24 @@ add_task(async function testPolicyPermis
     let removeButton = permissionsList.querySelector(".identity-popup-permission-remove-button");
     ok(removeButton == null, "The permission remove button is not visible");
 
     Services.perms.removeAll();
     await closeIdentityPopup();
   });
 });
 
+add_task(async function testHiddenAfterRefresh() {
+  await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(browser) {
+
+    ok(BrowserTestUtils.is_hidden(gIdentityHandler._identityPopup), "Popup is hidden");
+
+    await openIdentityPopup();
+
+    ok(!BrowserTestUtils.is_hidden(gIdentityHandler._identityPopup), "Popup is shown");
+
+    let reloaded = BrowserTestUtils.browserLoaded(browser, false, PERMISSIONS_PAGE);
+    EventUtils.synthesizeKey("VK_F5", {}, browser.ownerGlobal);
+    await reloaded;
+
+    ok(BrowserTestUtils.is_hidden(gIdentityHandler._identityPopup), "Popup is hidden");
+  });
+});
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -144,29 +144,68 @@ menuitem.bookmark-item {
   opacity: 0.7;
 }
 
 %include ../shared/toolbarbuttons.inc.css
 %include ../shared/toolbarbutton-icons.inc.css
 %include ../shared/menupanel.inc.css
 
 /* Fullscreen window controls */
-#window-controls {
-  -moz-box-align: start;
-  margin-inline-start: 10px;
+
+#minimize-button,
+#restore-button,
+#close-button {
+  -moz-appearance: none;
+  border: none;
+  margin: 0 !important;
+  padding: 6px 12px;
+  -moz-context-properties: stroke;
+  stroke: currentColor;
+  color: inherit;
 }
 
 #minimize-button {
-  list-style-image: url("chrome://global/skin/icons/Minimize.gif");
+  list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
 }
+
 #restore-button {
-  list-style-image: url("chrome://global/skin/icons/Restore.gif");
+  list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
+}
+
+#minimize-button:hover,
+#restore-button:hover {
+  background-color: hsla(0,0%,0%,.12);
+}
+
+#minimize-button:hover:active,
+#restore-button:hover:active {
+  background-color: hsla(0,0%,0%,.22);
+}
+
+#TabsToolbar[brighttext] > #window-controls > #minimize-button:hover,
+#TabsToolbar[brighttext] > #window-controls > #restore-button:hover {
+  background-color: hsla(0,0%,100%,.12);
 }
+
+#TabsToolbar[brighttext] > #window-controls > #minimize-button:hover:active,
+#TabsToolbar[brighttext] > #window-controls > #restore-button:hover:active {
+  background-color: hsla(0,0%,100%,.22);
+}
+
 #close-button {
-  list-style-image: url("chrome://global/skin/icons/Close.gif");
+  list-style-image: url(chrome://browser/skin/window-controls/close.svg);
+}
+
+#close-button:hover {
+  background-color: hsl(355, 86%, 49%);
+  stroke: white;
+}
+
+#close-button:hover:active {
+  background-color: hsl(355, 82%, 69%);
 }
 
 /* Location bar */
 
 %include ../shared/urlbar-searchbar.inc.css
 
 #urlbar:not(:-moz-lwtheme):not([focused="true"]),
 .searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -37,15 +37,18 @@ browser.jar:
   skin/classic/browser/places/organizer.xml           (places/organizer.xml)
   skin/classic/browser/places/toolbarDropMarker.png   (places/toolbarDropMarker.png)
   skin/classic/browser/preferences/alwaysAsk.png      (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/preferences.css    (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/applications.css   (preferences/applications.css)
   skin/classic/browser/tabbrowser/tabDragIndicator.png      (tabbrowser/tabDragIndicator.png)
+  skin/classic/browser/window-controls/close.svg                 (window-controls/close.svg)
+  skin/classic/browser/window-controls/minimize.svg              (window-controls/minimize.svg)
+  skin/classic/browser/window-controls/restore.svg               (window-controls/restore.svg)
 
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
 
 % override chrome://browser/skin/feeds/audioFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/videoFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/window-controls/close.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <path stroke="context-stroke" stroke-width=".9" fill="none" d="M1,1 l 10,10 M1,11 l 10,-10"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/window-controls/minimize.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <line stroke="context-stroke" stroke-width=".9" fill="none" shape-rendering="crispEdges" x1="1" y1="5.5" x2="11" y2="5.5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/window-controls/restore.svg
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" stroke="context-stroke" stroke-width=".9" fill="none" shape-rendering="crispEdges">
+  <rect x="1.5" y="3.5" width="7" height="7"/>
+  <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
+</svg>
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -10,17 +10,17 @@ import os
 import posixpath
 import tempfile
 import shutil
 import sys
 
 from automation import Automation
 from mozdevice import ADBTimeoutError
 from mozlog import get_default_logger
-from mozscreenshot import dump_screen
+from mozscreenshot import dump_screen, dump_device_screen
 import mozcrash
 
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = ["The character encoding of the HTML document was not declared",
                        "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                        "Unexpected value from nativeGetEnabledTags: 0"]
 
 
@@ -101,41 +101,44 @@ class RemoteAutomation(Automation):
         if status == 2:
             print("TEST-UNEXPECTED-FAIL | %s | application timed out after %d "
                   "seconds with no output"
                   % (self.lastTestSeen, int(timeout)))
 
         return status
 
     def deleteANRs(self):
-        # empty ANR traces.txt file; usually need root permissions
-        # we make it empty and writable so we can test the ANR reporter later
-        traces = "/data/anr/traces.txt"
+        # Remove files from the dalvik stack-trace directory.
+        if not self._device.is_dir(self._device.stack_trace_dir, root=True):
+            return
         try:
-            self._device.shell_output('echo > %s' % traces, root=True)
-            self._device.shell_output('chmod 666 %s' % traces, root=True)
+            for trace_file in self._device.ls(self._device.stack_trace_dir, root=True):
+                trace_path = posixpath.join(self._device.stack_trace_dir, trace_file)
+                self._device.chmod(trace_path, root=True)
+                self._device.rm(trace_path, root=True)
         except Exception as e:
-            print("Error deleting %s: %s" % (traces, str(e)))
+            print("Error deleting %s: %s" % (self._device.stack_trace_dir, str(e)))
 
     def checkForANRs(self):
-        traces = "/data/anr/traces.txt"
-        if self._device.is_file(traces):
-            try:
-                t = self._device.get_file(traces)
+        if not self._device.is_dir(self._device.stack_trace_dir):
+            print("%s not found" % self._device.stack_trace_dir)
+            return
+        try:
+            for trace_file in self._device.ls(self._device.stack_trace_dir, root=True):
+                trace_path = posixpath.join(self._device.stack_trace_dir, trace_file)
+                t = self._device.get_file(trace_path)
                 if t:
                     stripped = t.strip()
                     if len(stripped) > 0:
-                        print("Contents of %s:" % traces)
+                        print("Contents of %s:" % trace_path)
                         print(t)
-                # Once reported, delete traces
-                self.deleteANRs()
-            except Exception as e:
-                print("Error pulling %s: %s" % (traces, str(e)))
-        else:
-            print("%s not found" % traces)
+            # Once reported, delete traces
+            self.deleteANRs()
+        except Exception as e:
+            print("Error pulling %s: %s" % (self._device.stack_trace_dir, str(e)))
 
     def deleteTombstones(self):
         # delete any tombstone files from device
         self._device.rm("/data/tombstones", force=True,
                         recursive=True, root=True)
 
     def checkForTombstones(self):
         # pull any tombstones from device and move to MOZ_UPLOAD_DIR
@@ -233,16 +236,20 @@ class RemoteAutomation(Automation):
             self.device = device
             self.lastTestSeen = "remoteautomation.py"
             self.messageLogger = messageLogger
             self.proc = stdout
             self.procName = cmd[0].split(posixpath.sep)[-1]
             self.stdoutlen = 0
             self.utilityPath = None
 
+            if app and self.device.process_exist(app):
+                print("remoteautomation.py %s is already running. Stopping...")
+                self.device.stop_application(app, root=True)
+
             self.counts = counts
             if self.counts is not None:
                 self.counts['pass'] = 0
                 self.counts['fail'] = 0
                 self.counts['todo'] = 0
 
             if cmd[0] == 'am':
                 cmd = ' '.join(cmd)
@@ -402,21 +409,24 @@ class RemoteAutomation(Automation):
                         print("Failed to get top activity, retrying, once...")
                         top = self.device.get_top_activity(timeout=60)
             # Flush anything added to stdout during the sleep
             self.read_stdout()
             print("wait for %s complete; top activity=%s" % (self.procName, top))
             return status
 
         def kill(self, stagedShutdown=False):
-            if self.utilityPath:
-                # Take a screenshot to capture the screen state just before
-                # the application is killed. There are on-device screenshot
-                # options but they rarely work well with Firefox on the
-                # Android emulator. dump_screen provides an effective
+            # Take a screenshot to capture the screen state just before
+            # the application is killed.
+            if not self.device._device_serial.startswith('emulator-'):
+                dump_device_screen(self.device, get_default_logger())
+            elif self.utilityPath:
+                # Do not use the on-device screenshot options since
+                # they rarely work well with Firefox on the Android
+                # emulator. dump_screen provides an effective
                 # screenshot of the emulator and its host desktop.
                 dump_screen(self.utilityPath, get_default_logger())
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 try:
                     self.device.pkill(self.procName, sig=3, attempts=1)
                 except ADBTimeoutError:
                     raise
deleted file mode 100644
--- a/dom/animation/test/css-transitions/test_animation-pausing.html
+++ /dev/null
@@ -1,50 +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>
-<body>
-<div id="log"></div>
-<script>
-'use strict';
-
-async_test(function(t) {
-  var div = addDiv(t);
-  var cs = getComputedStyle(div);
-
-  div.style.marginLeft = '0px';
-  cs.marginLeft; // Flush style to set up transition start point
-  div.style.transition = 'margin-left 100s';
-  div.style.marginLeft = '10000px';
-  cs.marginLeft;
-
-  var animation = div.getAnimations()[0];
-  var previousProgress = animation.effect.getComputedTiming().progress;
-  assert_equals(previousProgress, 0, 'Initial value of progress is zero');
-
-  animation.ready.then(waitForNextFrame).then(t.step_func(function() {
-    assert_greater_than(animation.effect.getComputedTiming().progress,
-                        previousProgress,
-                        'Iteration progress is initially increasing');
-    animation.pause();
-    return animation.ready;
-  })).then(t.step_func(function() {
-    previousProgress = animation.effect.getComputedTiming().progress;
-    return waitForNextFrame();
-  })).then(t.step_func(function() {
-    assert_equals(animation.effect.getComputedTiming().progress,
-                  previousProgress,
-                  'Iteration progress does not increase after calling pause()');
-    previousProgress = animation.effect.getComputedTiming().progress;
-    animation.play();
-    return animation.ready.then(waitForNextFrame);
-  })).then(t.step_func(function() {
-    assert_greater_than(animation.effect.getComputedTiming().progress,
-                        previousProgress,
-                        'Iteration progress increases after calling play()');
-    t.done();
-  }));
-}, 'pause() and play() a transition');
-
-</script>
-</body>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -17,31 +17,16 @@ support-files =
   mozilla/file_disable_animations_api_timelines.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-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]
-[css-transitions/test_document-get-animations.html]
-[css-transitions/test_effect-target.html]
-[css-transitions/test_element-get-animations.html]
-[css-transitions/test_event-dispatch.html]
-[css-transitions/test_keyframeeffect-getkeyframes.html]
-[css-transitions/test_pseudoElement-get-animations.html]
-[css-transitions/test_setting-effect.html]
 [document-timeline/test_document-timeline.html]
 skip-if = (verify && !debug && (os == 'mac'))
 [document-timeline/test_request_animation_frame.html]
 [mozilla/test_cascade.html]
 [mozilla/test_cubic_bezier_limits.html]
 [mozilla/test_deferred_start.html]
 skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1363957
 [mozilla/test_disable_animations_api_compositing.html]
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1606,71 +1606,90 @@ JSStructuredCloneWriter::startWrite(Hand
             return false;
         if (backref)
             return true;
 
         ESClass cls;
         if (!GetBuiltinClass(context(), obj, &cls))
             return false;
 
-        if (cls == ESClass::RegExp) {
+        switch (cls) {
+          case ESClass::Object:
+          case ESClass::Array:
+            return traverseObject(obj);
+          case ESClass::Number: {
+            RootedValue unboxed(context());
+            if (!Unbox(context(), obj, &unboxed))
+                return false;
+            return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
+          }
+          case ESClass::String: {
+            RootedValue unboxed(context());
+            if (!Unbox(context(), obj, &unboxed))
+                return false;
+            return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
+          }
+          case ESClass::Boolean: {
+            RootedValue unboxed(context());
+            if (!Unbox(context(), obj, &unboxed))
+                return false;
+            return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
+          }
+          case ESClass::RegExp: {
             RegExpShared* re = RegExpToShared(context(), obj);
             if (!re)
                 return false;
             return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
                    writeString(SCTAG_STRING, re->getSource());
-        } else if (cls == ESClass::Date) {
+          }
+          case ESClass::ArrayBuffer: {
+            if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj))
+                return writeArrayBuffer(obj);
+            break;
+          }
+          case ESClass::SharedArrayBuffer:
+            if (JS_IsSharedArrayBufferObject(obj))
+                return writeSharedArrayBuffer(obj);
+            break;
+          case ESClass::Date: {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
-        } else if (JS_IsTypedArrayObject(obj)) {
-            return writeTypedArray(obj);
-        } else if (JS_IsDataViewObject(obj)) {
-            return writeDataView(obj);
-        } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
-            return writeArrayBuffer(obj);
-        } else if (JS_IsSharedArrayBufferObject(obj)) {
-            return writeSharedArrayBuffer(obj);
-        } else if (wasm::IsSharedWasmMemoryObject(obj)) {
-            return writeSharedWasmMemory(obj);
-        } else if (cls == ESClass::Object) {
-            return traverseObject(obj);
-        } else if (cls == ESClass::Array) {
-            return traverseObject(obj);
-        } else if (cls == ESClass::Boolean) {
-            RootedValue unboxed(context());
-            if (!Unbox(context(), obj, &unboxed))
-                return false;
-            return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
-        } else if (cls == ESClass::Number) {
-            RootedValue unboxed(context());
-            if (!Unbox(context(), obj, &unboxed))
-                return false;
-            return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
-        } else if (cls == ESClass::String) {
-            RootedValue unboxed(context());
-            if (!Unbox(context(), obj, &unboxed))
-                return false;
-            return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
-        } else if (cls == ESClass::Map) {
+          }
+          case ESClass::Set:
+            return traverseSet(obj);
+          case ESClass::Map:
             return traverseMap(obj);
-        } else if (cls == ESClass::Set) {
-            return traverseSet(obj);
-        }
 #ifdef ENABLE_BIGINT
-        else if (cls == ESClass::BigInt) {
+          case ESClass::BigInt: {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return writeBigInt(SCTAG_BIGINT_OBJECT, unboxed.toBigInt());
-        }
+          }
 #endif
-        else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
-            return traverseSavedFrame(obj);
+          case ESClass::Promise:
+          case ESClass::MapIterator:
+          case ESClass::SetIterator:
+          case ESClass::Arguments:
+          case ESClass::Error:
+            break;
+
+          case ESClass::Other: {
+            if (JS_IsTypedArrayObject(obj))
+                return writeTypedArray(obj);
+            if (JS_IsDataViewObject(obj))
+                return writeDataView(obj);
+            if (wasm::IsSharedWasmMemoryObject(obj))
+                return writeSharedWasmMemory(obj);
+            if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj))
+                return traverseSavedFrame(obj);
+            break;
+          }
         }
 
         if (out.buf.callbacks_ && out.buf.callbacks_->write)
             return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_);
         // else fall through
     }
 
     return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
@@ -1850,55 +1869,58 @@ JSStructuredCloneWriter::transferOwnersh
 }
 
 bool
 JSStructuredCloneWriter::write(HandleValue v)
 {
     if (!startWrite(v))
         return false;
 
+    RootedObject obj(context());
+    RootedValue key(context());
+    RootedValue val(context());
+    RootedId id(context());
+
     while (!counts.empty()) {
-        RootedObject obj(context(), &objs.back().toObject());
+        obj = &objs.back().toObject();
         assertSameCompartment(context(), obj);
         if (counts.back()) {
             counts.back()--;
-            RootedValue key(context(), entries.back());
+            key = entries.back();
             entries.popBack();
             checkStack();
 
             ESClass cls;
             if (!GetBuiltinClass(context(), obj, &cls))
                 return false;
 
             if (cls == ESClass::Map) {
                 counts.back()--;
-                RootedValue val(context(), entries.back());
+                val = entries.back();
                 entries.popBack();
                 checkStack();
 
                 if (!startWrite(key) || !startWrite(val))
                     return false;
             } else if (cls == ESClass::Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
                 if (!startWrite(key))
                     return false;
             } else {
-                RootedId id(context());
                 if (!ValueToId<CanGC>(context(), key, &id))
                   return false;
                 MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
 
                 // If obj still has an own property named id, write it out.
                 // The cost of re-checking could be avoided by using
                 // NativeIterators.
                 bool found;
                 if (!HasOwnProperty(context(), obj, id, &found))
                     return false;
 
                 if (found) {
-                    RootedValue val(context());
                     if (!startWrite(key) ||
                         !GetProperty(context(), obj, obj, id, &val) ||
                         !startWrite(val))
                     {
                         return false;
                     }
                 }
             }
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -245,16 +245,26 @@ nsHTMLButtonControlFrame::ReflowButtonCo
   ReflowInput contentsReflowInput(aPresContext, aButtonReflowInput,
                                   aFirstKid, availSize);
 
   nsReflowStatus contentsReflowStatus;
   ReflowOutput contentsDesiredSize(aButtonReflowInput);
   childPos.B(wm) = 0; // This will be set properly later, after reflowing the
                       // child to determine its size.
 
+  const LayoutFrameType frameType = aFirstKid->Type();
+  if (frameType == LayoutFrameType::FlexContainer ||
+      frameType == LayoutFrameType::GridContainer) {
+    contentsReflowInput.ComputedBSize() = aButtonReflowInput.ComputedBSize();
+    contentsReflowInput.ComputedMinBSize() =
+      aButtonReflowInput.ComputedMinBSize();
+    contentsReflowInput.ComputedMaxBSize() =
+      aButtonReflowInput.ComputedMaxBSize();
+  }
+
   // We just pass a dummy containerSize here, as the child will be
   // repositioned later by FinishReflowChild.
   nsSize dummyContainerSize;
   ReflowChild(aFirstKid, aPresContext,
               contentsDesiredSize, contentsReflowInput,
               wm, childPos, dummyContainerSize, 0, contentsReflowStatus);
   MOZ_ASSERT(contentsReflowStatus.IsComplete(),
              "We gave button-contents frame unconstrained available height, "
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/button-display-flex-fullsize-1-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>Reference: Testing for full height flex container in a button.</title>
+  <meta charset="utf-8">
+  <style>
+    button {
+      vertical-align: top;
+      padding: 0;
+      border: solid 1px black;
+      background: lightblue;
+      width: 200px;
+      height: 200px;
+    }
+
+    .flex {
+      display: flex;
+      justify-content: space-between;
+      align-items: stretch;
+      width: 100%;
+      height: 100%;
+    }
+
+    .flex > * {
+      margin: 1px;
+      background: teal;
+      min-height: 10px;
+      min-width: 10px;
+    }
+
+    .vertical {
+      flex-direction: column;
+    }
+  </style>
+</head>
+<body>
+  <button>
+    <div class="flex">
+     <div></div>
+     <div></div>
+     <div></div>
+    </div>
+  </button>
+  <button>
+    <div class="flex vertical">
+     <div></div>
+     <div></div>
+     <div></div>
+    </div>
+  </button>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/button-display-flex-fullsize-1.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>CSS Test: Testing for full height flex container in a button.</title>
+  <meta charset="utf-8">
+  <style>
+    button {
+      vertical-align: top;
+      padding: 0;
+      border: solid 1px black;
+      background: lightblue;
+      width: 200px;
+      height: 200px;
+    }
+
+    .flex {
+      display: inline-flex;
+      justify-content: space-between;
+      align-items: stretch;
+    }
+
+    .flex > * {
+      margin: 1px;
+      background: teal;
+      min-height: 10px;
+      min-width: 10px;
+    }
+
+    .vertical {
+      flex-direction: column;
+    }
+  </style>
+</head>
+<body>
+  <button class="flex">
+    <div></div>
+    <div></div>
+    <div></div>
+  </button>
+  <button class="flex vertical">
+    <div></div>
+    <div></div>
+    <div></div>
+  </button>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/button-display-grid-fullsize-1-ref.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>Reference: Testing for full height grid container in a button.</title>
+  <meta charset="utf-8">
+  <style>
+    button {
+      vertical-align: top;
+      padding: 0;
+      border: solid 1px black;
+      background: lightblue;
+      width: 200px;
+      height: 200px;
+    }
+
+    .grid {
+      display: grid;
+      grid-template-columns: auto auto auto;
+      grid-template-rows: auto;
+      justify-content: space-between;
+      align-items: stretch;
+      width: 100%;
+      height: 100%;
+    }
+
+    .grid > * {
+      margin: 1px;
+      background: teal;
+      min-height: 10px;
+      min-width: 10px;
+    }
+
+    .vertical {
+      grid-template-columns: 1fr;
+      grid-template-rows: 1fr 1fr 1fr;
+    }
+  </style>
+</head>
+<body>
+  <button>
+    <div class="grid">
+      <div></div>
+      <div></div>
+      <div></div>
+    </div>
+  </button>
+  <button>
+    <div class="grid vertical">
+      <div></div>
+      <div></div>
+      <div></div>
+    </div>
+  </button>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/button-display-grid-fullsize-1.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>CSS Test: Testing for full height grid container in a button.</title>
+  <meta charset="utf-8">
+  <style>
+    button {
+      vertical-align: top;
+      padding: 0;
+      border: solid 1px black;
+      background: lightblue;
+      width: 200px;
+      height: 200px;
+    }
+
+    .grid {
+      display: inline-grid;
+      grid-template-columns: auto auto auto;
+      grid-template-rows: auto;
+      justify-content: space-between;
+      align-items: stretch;
+    }
+
+    .grid > * {
+      margin: 1px;
+      background: teal;
+      min-height: 10px;
+      min-width: 10px;
+    }
+
+    .vertical {
+      grid-template-columns: 1fr;
+      grid-template-rows: 1fr 1fr 1fr;
+    }
+  </style>
+</head>
+<body>
+  <!-- 3 columns/1 row -->
+  <button class="grid">
+    <div></div>
+    <div></div>
+    <div></div>
+  </button>
+  <!-- 1 column/3 rows, using "fr" units to fill container -->
+  <button class="grid vertical">
+    <div></div>
+    <div></div>
+    <div></div>
+  </button>
+</body>
+</html>
+
--- a/layout/reftests/forms/button/reftest.list
+++ b/layout/reftests/forms/button/reftest.list
@@ -1,8 +1,12 @@
+# Tests for grid and flex sizing in buttons
+== button-display-flex-fullsize-1.html button-display-flex-fullsize-1-ref.html
+== button-display-grid-fullsize-1.html button-display-grid-fullsize-1-ref.html
+
 == first-letter-1.html first-letter-1-ref.html
 != first-letter-1.html first-letter-1-noref.html
 
 == max-height.html max-height-ref.html
 == min-height.html min-height-ref.html
 
 # Android is off ever-so-slightly on the points where the text
 # runs into the border on this text, so a little fuzz is needed.
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -43,17 +43,17 @@ class ReftestRunner(MozbuildObject):
 
     def _make_shell_string(self, s):
         return "'%s'" % re.sub("'", r"'\''", s)
 
     def _setup_objdir(self, args):
         # reftest imports will happen from the objdir
         sys.path.insert(0, self.reftest_dir)
 
-        if not args.tests:
+        if args.suite != 'jstestbrowser' and not args.tests:
             test_subdir = {
                 "reftest": os.path.join('layout', 'reftests'),
                 "crashtest": os.path.join('layout', 'crashtest'),
             }[args.suite]
             args.tests = [test_subdir]
 
         tests = os.path.join(self.reftest_dir, 'tests')
         if not os.path.isdir(tests):
@@ -224,14 +224,17 @@ class MachCommands(MachCommandBase):
     def run_crashtest(self, **kwargs):
         kwargs["suite"] = "crashtest"
         return self._run_reftest(**kwargs)
 
     def _run_reftest(self, **kwargs):
         kwargs["topsrcdir"] = self.topsrcdir
         process_test_objects(kwargs)
         reftest = self._spawn(ReftestRunner)
+        # Unstructured logging must be enabled prior to calling
+        # adb which uses an unstructured logger in its constructor.
+        reftest.log_manager.enable_unstructured()
         if conditions.is_android(self):
             from mozrunner.devices.android_device import verify_android_device
             verify_android_device(self, install=True, xre=True, app=kwargs["app"],
                                   device_serial=kwargs["deviceSerial"])
             return reftest.run_android_test(**kwargs)
         return reftest.run_desktop_test(**kwargs)
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -148,17 +148,16 @@ class RemoteReftest(RefTest):
         verbose = False
         if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
             verbose = True
             print "set verbose!"
         self.device = ADBAndroid(adb=options.adb_path or 'adb',
                                  device=options.deviceSerial,
                                  test_root=options.remoteTestRoot,
                                  verbose=verbose)
-
         if options.remoteTestRoot is None:
             options.remoteTestRoot = posixpath.join(self.device.test_root, "reftest")
         options.remoteProfile = posixpath.join(options.remoteTestRoot, "profile")
         options.remoteLogFile = posixpath.join(options.remoteTestRoot, "reftest.log")
         options.logFile = options.remoteLogFile
         self.remoteProfile = options.remoteProfile
         self.remoteTestRoot = options.remoteTestRoot
 
@@ -317,27 +316,16 @@ class RemoteReftest(RefTest):
             self.device.push(profileDir, options.remoteProfile)
             self.device.chmod(options.remoteProfile, recursive=True, root=True)
         except Exception:
             print "Automation Error: Failed to copy profiledir to device"
             raise
 
         return profile
 
-    def copyExtraFilesToProfile(self, options, profile):
-        profileDir = profile.profile
-        RefTest.copyExtraFilesToProfile(self, options, profile)
-        if len(os.listdir(profileDir)) > 0:
-            try:
-                self.device.push(profileDir, options.remoteProfile)
-                self.device.chmod(options.remoteProfile, recursive=True, root=True)
-            except Exception:
-                print "Automation Error: Failed to copy extra files to device"
-                raise
-
     def printDeviceInfo(self, printLogcat=False):
         try:
             if printLogcat:
                 logcat = self.device.get_logcat(filter_out_regexps=fennecLogcatFilters)
                 for l in logcat:
                     print "%s\n" % l.decode('utf-8', 'replace')
             print "Device info:"
             devinfo = self.device.get_info()
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -44,17 +44,17 @@ itoa = "0.4"
 lazy_static = "1"
 log = "0.4"
 malloc_size_of = { path = "../malloc_size_of" }
 malloc_size_of_derive = { path = "../malloc_size_of_derive" }
 matches = "0.1"
 nsstring = {path = "../../support/gecko/nsstring", optional = true}
 num_cpus = {version = "1.1.0", optional = true}
 num-integer = "0.1"
-num-traits = "0.1"
+num-traits = "0.2"
 num-derive = "0.2"
 new-ordered-float = "1.0"
 owning_ref = "0.3.3"
 parking_lot = "0.6"
 precomputed-hash = "0.1.1"
 rayon = "1"
 selectors = { path = "../selectors" }
 serde = {version = "1.0", optional = true, features = ["derive"]}
--- a/servo/components/style/gecko/media_features.rs
+++ b/servo/components/style/gecko/media_features.rs
@@ -3,35 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Gecko's media feature list and evaluator.
 
 use Atom;
 use app_units::Au;
 use euclid::Size2D;
 use gecko_bindings::bindings;
+use media_queries::Device;
+use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
+use media_queries::media_feature::{MediaFeatureDescription, Evaluator};
+use media_queries::media_feature_expression::{AspectRatio, RangeOrOperator};
 use values::computed::CSSPixelLength;
 use values::computed::Resolution;
 
-use media_queries::Device;
-use media_queries::media_feature::{MediaFeatureDescription, Evaluator};
-use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
-use media_queries::media_feature_expression::{AspectRatio, RangeOrOperator};
-
-macro_rules! feature {
-    ($name:expr, $allows_ranges:expr, $evaluator:expr, $reqs:expr,) => {
-        MediaFeatureDescription {
-            name: $name,
-            allows_ranges: $allows_ranges,
-            evaluator: $evaluator,
-            requirements: $reqs,
-        }
-    }
-}
-
 fn viewport_size(device: &Device) -> Size2D<Au> {
     let pc = device.pres_context();
     if pc.mIsRootPaginatedDocument() != 0 {
         // We want the page size, including unprintable areas and margins.
         // FIXME(emilio, bug 1414600): Not quite!
         let area = &pc.mPageSize;
         return Size2D::new(Au(area.width), Au(area.height))
     }
@@ -152,17 +140,17 @@ fn eval_device_pixel_ratio(
 ) -> bool {
     eval_resolution(
         device,
         query_value.map(Resolution::from_dppx),
         range_or_operator,
     )
 }
 
-#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
+#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
 #[repr(u8)]
 enum Orientation {
     Landscape,
     Portrait,
 }
 
 fn eval_orientation_for<F>(
     device: &Device,
@@ -199,17 +187,17 @@ fn eval_orientation(
 fn eval_device_orientation(
     device: &Device,
     value: Option<Orientation>,
 ) -> bool {
     eval_orientation_for(device, value, device_size)
 }
 
 /// Values for the display-mode media feature.
-#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
+#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
 #[repr(u8)]
 #[allow(missing_docs)]
 pub enum DisplayMode {
   Browser = 0,
   MinimalUi,
   Standalone,
   Fullscreen,
 }
@@ -245,17 +233,17 @@ fn eval_transform_3d(
     _: &Device,
     query_value: Option<bool>,
     _: Option<RangeOrOperator>,
 ) -> bool {
     let supports_transforms = true;
     query_value.map_or(supports_transforms, |v| v == supports_transforms)
 }
 
-#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
+#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
 #[repr(u8)]
 enum Scan {
     Progressive,
     Interlace,
 }
 
 /// https://drafts.csswg.org/mediaqueries-4/#scan
 fn eval_scan(_: &Device, _: Option<Scan>) -> bool {
@@ -320,17 +308,17 @@ fn eval_resolution(
         unsafe { bindings::Gecko_MediaFeatures_GetResolution(device.document()) };
     RangeOrOperator::evaluate(
         range_or_operator,
         query_value.map(|r| r.dppx()),
         resolution_dppx,
     )
 }
 
-#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
+#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
 #[repr(u8)]
 enum PrefersReducedMotion {
     NoPreference,
     Reduce,
 }
 
 /// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion
 fn eval_prefers_reduced_motion(
--- a/servo/components/style/media_queries/media_feature.rs
+++ b/servo/components/style/media_queries/media_feature.rs
@@ -1,22 +1,21 @@
 /* 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/. */
 
 //! Media features.
 
-use super::Device;
-use super::media_feature_expression::{AspectRatio, RangeOrOperator};
-
 use Atom;
 use cssparser::Parser;
 use parser::ParserContext;
 use std::fmt;
 use style_traits::ParseError;
+use super::Device;
+use super::media_feature_expression::{AspectRatio, RangeOrOperator};
 use values::computed::{CSSPixelLength, Resolution};
 
 /// A generic discriminant for an enum value.
 pub type KeywordDiscriminant = u8;
 
 type MediaFeatureEvaluator<T> = fn(
     device: &Device,
     // null == no value was given in the query.
@@ -127,17 +126,17 @@ bitflags! {
         const WEBKIT_PREFIX = 1 << 1;
         /// The feature requires the webkit-device-pixel-ratio preference to be
         /// enabled.
         const WEBKIT_DEVICE_PIXEL_RATIO_PREF_ENABLED = 1 << 2;
     }
 }
 
 /// Whether a media feature allows ranges or not.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 #[allow(missing_docs)]
 pub enum AllowsRanges {
     Yes,
     No,
 }
 
 /// A description of a media feature.
 pub struct MediaFeatureDescription {
@@ -156,16 +155,28 @@ pub struct MediaFeatureDescription {
 impl MediaFeatureDescription {
     /// Whether this media feature allows ranges.
     #[inline]
     pub fn allows_ranges(&self) -> bool {
         self.allows_ranges == AllowsRanges::Yes
     }
 }
 
+/// A simple helper to construct a `MediaFeatureDescription`.
+macro_rules! feature {
+    ($name:expr, $allows_ranges:expr, $evaluator:expr, $reqs:expr,) => {
+        $crate::media_queries::media_feature::MediaFeatureDescription {
+            name: $name,
+            allows_ranges: $allows_ranges,
+            evaluator: $evaluator,
+            requirements: $reqs,
+        }
+    }
+}
+
 impl fmt::Debug for MediaFeatureDescription {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_struct("MediaFeatureExpression")
             .field("name", &self.name)
             .field("allows_ranges", &self.allows_ranges)
             .field("requirements", &self.requirements)
             .finish()
     }
--- a/servo/components/style/media_queries/media_feature_expression.rs
+++ b/servo/components/style/media_queries/media_feature_expression.rs
@@ -1,40 +1,33 @@
 /* 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/. */
 
 //! Parsing for media feature expressions, like `(foo: bar)` or
 //! `(width >= 400px)`.
 
-use super::Device;
-use super::media_feature::{Evaluator, MediaFeatureDescription};
-use super::media_feature::{ParsingRequirements, KeywordDiscriminant};
-
 use Atom;
+use context::QuirksMode;
 use cssparser::{Parser, Token};
-use context::QuirksMode;
+#[cfg(feature = "gecko")]
+use gecko_bindings::structs;
 use num_traits::Zero;
 use parser::{Parse, ParserContext};
 use std::cmp::{PartialOrd, Ordering};
 use std::fmt::{self, Write};
 use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use stylesheets::Origin;
+use super::Device;
+use super::media_feature::{Evaluator, MediaFeatureDescription};
+use super::media_feature::{ParsingRequirements, KeywordDiscriminant};
 use values::{serialize_atom_identifier, CSSFloat};
-use values::specified::{Integer, Length, Number, Resolution};
 use values::computed::{self, ToComputedValue};
-
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs;
-
-#[cfg(feature = "gecko")]
-use gecko::media_features::MEDIA_FEATURES;
-#[cfg(feature = "servo")]
-use servo::media_queries::MEDIA_FEATURES;
+use values::specified::{Integer, Length, Number, Resolution};
 
 /// An aspect ratio, with a numerator and denominator.
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct AspectRatio(pub u32, pub u32);
 
 impl ToCss for AspectRatio {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
@@ -282,16 +275,21 @@ impl MediaFeatureExpression {
     }
 
     /// Parse a media feature expression where we've already consumed the
     /// parenthesis.
     pub fn parse_in_parenthesis_block<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
+        #[cfg(feature = "gecko")]
+        use gecko::media_features::MEDIA_FEATURES;
+        #[cfg(feature = "servo")]
+        use servo::media_queries::MEDIA_FEATURES;
+
         // FIXME: remove extra indented block when lifetimes are non-lexical
         let feature;
         let range;
         {
             let location = input.current_source_location();
             let ident = input.expect_ident()?;
 
             let mut requirements = ParsingRequirements::empty();
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -1,28 +1,27 @@
 /* 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/. */
 
 //! Servo's media-query device and expression representation.
 
 use app_units::Au;
-use context::QuirksMode;
-use cssparser::{Parser, RGBA};
+use cssparser::RGBA;
 use euclid::{Size2D, TypedScale, TypedSize2D};
 use media_queries::MediaType;
-use parser::ParserContext;
+use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
+use media_queries::media_feature::{MediaFeatureDescription, Evaluator};
+use media_queries::media_feature_expression::RangeOrOperator;
 use properties::ComputedValues;
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
 use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
-use style_traits::{CSSPixel, CssWriter, DevicePixel, ParseError, ToCss};
+use style_traits::{CSSPixel, DevicePixel};
 use style_traits::viewport::ViewportConstraints;
-use values::{specified, KeyframesName};
-use values::computed::{self, ToComputedValue};
+use values::KeyframesName;
+use values::computed::CSSPixelLength;
 use values::computed::font::FontSize;
 
 /// A device is a structure that represents the current media a given document
 /// is displayed in.
 ///
 /// This is the struct against which media queries are evaluated.
 #[derive(Debug, MallocSizeOf)]
 pub struct Device {
@@ -150,130 +149,52 @@ impl Device {
     }
 
     /// Returns the default background color.
     pub fn default_background_color(&self) -> RGBA {
         RGBA::new(255, 255, 255, 255)
     }
 }
 
-/// A expression kind servo understands and parses.
-///
-/// Only `pub` for unit testing, please don't use it directly!
-#[derive(Clone, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub enum ExpressionKind {
-    /// <http://dev.w3.org/csswg/mediaqueries-3/#width>
-    Width(Range<specified::Length>),
+/// https://drafts.csswg.org/mediaqueries-4/#width
+fn eval_width(
+    device: &Device,
+    value: Option<CSSPixelLength>,
+    range_or_operator: Option<RangeOrOperator>,
+) -> bool {
+    RangeOrOperator::evaluate(
+        range_or_operator,
+        value.map(Au::from),
+        device.au_viewport_size().width,
+    )
+}
+
+#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
+#[repr(u8)]
+enum Scan {
+    Progressive,
+    Interlace,
 }
 
-/// A single expression a per:
-///
-/// <http://dev.w3.org/csswg/mediaqueries-3/#media1>
-#[derive(Clone, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct MediaFeatureExpression(pub ExpressionKind);
-
-impl MediaFeatureExpression {
-    /// The kind of expression we're, just for unit testing.
-    ///
-    /// Eventually this will become servo-only.
-    pub fn kind_for_testing(&self) -> &ExpressionKind {
-        &self.0
-    }
-
-    /// Parse a media expression of the form:
-    ///
-    /// ```
-    /// media-feature: media-value
-    /// ```
-    ///
-    /// Only supports width ranges for now.
-    pub fn parse<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        input.expect_parenthesis_block()?;
-        input.parse_nested_block(|input| {
-            Self::parse_in_parenthesis_block(context, input)
-        })
-    }
-
-    /// Parse a media range expression where we've already consumed the
-    /// parenthesis.
-    pub fn parse_in_parenthesis_block<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        let name = input.expect_ident_cloned()?;
-        input.expect_colon()?;
-        // TODO: Handle other media features
-        Ok(MediaFeatureExpression(match_ignore_ascii_case! { &name,
-            "min-width" => {
-                ExpressionKind::Width(Range::Min(specified::Length::parse_non_negative(context, input)?))
-            },
-            "max-width" => {
-                ExpressionKind::Width(Range::Max(specified::Length::parse_non_negative(context, input)?))
-            },
-            "width" => {
-                ExpressionKind::Width(Range::Eq(specified::Length::parse_non_negative(context, input)?))
-            },
-            _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
-        }))
-    }
-
-    /// Evaluate this expression and return whether it matches the current
-    /// device.
-    pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
-        let viewport_size = device.au_viewport_size();
-        let value = viewport_size.width;
-        match self.0 {
-            ExpressionKind::Width(ref range) => {
-                match range.to_computed_range(device, quirks_mode) {
-                    Range::Min(ref width) => value >= *width,
-                    Range::Max(ref width) => value <= *width,
-                    Range::Eq(ref width) => value == *width,
-                }
-            },
-        }
-    }
+/// https://drafts.csswg.org/mediaqueries-4/#scan
+fn eval_scan(_: &Device, _: Option<Scan>) -> bool {
+    // Since we doesn't support the 'tv' media type, the 'scan' feature never
+    // matches.
+    false
 }
 
-impl ToCss for MediaFeatureExpression {
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        let (s, l) = match self.0 {
-            ExpressionKind::Width(Range::Min(ref l)) => ("(min-width: ", l),
-            ExpressionKind::Width(Range::Max(ref l)) => ("(max-width: ", l),
-            ExpressionKind::Width(Range::Eq(ref l)) => ("(width: ", l),
-        };
-        dest.write_str(s)?;
-        l.to_css(dest)?;
-        dest.write_char(')')
-    }
+lazy_static! {
+    /// A list with all the media features that Servo supports.
+    pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 2] = [
+        feature!(
+            atom!("width"),
+            AllowsRanges::Yes,
+            Evaluator::Length(eval_width),
+            ParsingRequirements::empty(),
+        ),
+        feature!(
+            atom!("scan"),
+            AllowsRanges::No,
+            keyword_evaluator!(eval_scan, Scan),
+            ParsingRequirements::empty(),
+        ),
+    ];
 }
-
-/// An enumeration that represents a ranged value.
-///
-/// Only public for testing, implementation details of `MediaFeatureExpression`
-/// may change for Stylo.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub enum Range<T> {
-    /// At least the inner value.
-    Min(T),
-    /// At most the inner value.
-    Max(T),
-    /// Exactly the inner value.
-    Eq(T),
-}
-
-impl Range<specified::Length> {
-    fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
-        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| match *self {
-            Range::Min(ref width) => Range::Min(Au::from(width.to_computed_value(&context))),
-            Range::Max(ref width) => Range::Max(Au::from(width.to_computed_value(&context))),
-            Range::Eq(ref width) => Range::Eq(Au::from(width.to_computed_value(&context))),
-        })
-    }
-}
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -11,18 +11,18 @@ sys.path.insert(
     0, os.path.abspath(
         os.path.realpath(
             os.path.dirname(__file__))))
 
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 from runtests import MochitestDesktop, MessageLogger
 from mochitest_options import MochitestArgumentParser
-
 from mozdevice import ADBAndroid, ADBTimeoutError
+from mozscreenshot import dump_screen, dump_device_screen
 import mozinfo
 
 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
 
 
 class MochiRemote(MochitestDesktop):
     localProfile = None
     logMessages = []
@@ -111,16 +111,27 @@ class MochiRemote(MochitestDesktop):
             if uploadDir and self.device.is_dir(self.remoteMozLog):
                 self.device.pull(self.remoteMozLog, uploadDir)
         self.device.rm(self.remoteLogFile, force=True)
         self.device.rm(self.remoteProfile, force=True, recursive=True)
         self.device.rm(self.remoteCache, force=True, recursive=True)
         MochitestDesktop.cleanup(self, options, final)
         self.localProfile = None
 
+    def dumpScreen(self, utilityPath):
+        if self.haveDumpedScreen:
+            self.log.info(
+                "Not taking screenshot here: see the one that was previously logged")
+            return
+        self.haveDumpedScreen = True
+        if self.device._device_serial.startswith('emulator-'):
+            dump_screen(utilityPath, self.log)
+        else:
+            dump_device_screen(self.device, self.log)
+
     def findPath(self, paths, filename=None):
         for path in paths:
             p = path
             if filename:
                 p = os.path.join(p, filename)
             if os.path.exists(self.getFullPath(p)):
                 return path
         return None
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -175,16 +175,17 @@ class ADBCommand(object):
         # catch early a missing or non executable adb command
         # and get the adb version while we are at it.
         try:
             output = subprocess.Popen([adb, 'version'],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE).communicate()
             re_version = re.compile(r'Android Debug Bridge version (.*)')
             self._adb_version = re_version.match(output[0]).group(1)
+
         except Exception as exc:
             raise ADBError('%s: %s is not executable.' % (exc, adb))
 
     def _get_logger(self, logger_name):
         logger = None
         try:
             import mozlog
             logger = mozlog.get_default_logger(logger_name)
@@ -558,16 +559,17 @@ class ADBDevice(ADBCommand):
 
         :raises: * ADBError
                  * ADBTimeoutError
                  * ValueError
         """
         ADBCommand.__init__(self, adb=adb, adb_host=adb_host,
                             adb_port=adb_port, logger_name=logger_name,
                             timeout=timeout, verbose=verbose)
+        self._logger.info('Using adb %s' % self._adb_version)
         self._device_serial = self._get_device_serial(device)
         self._initial_test_root = test_root
         self._test_root = None
         self._device_ready_retry_wait = device_ready_retry_wait
         self._device_ready_retry_attempts = device_ready_retry_attempts
         self._have_root_shell = False
         self._have_su = False
         self._have_android_su = False
@@ -592,19 +594,23 @@ class ADBDevice(ADBCommand):
         # Do we have a 'Superuser' sh like su?
         try:
             if self.shell_output("su -c id", timeout=timeout).find(uid) != -1:
                 self._have_su = True
                 self._logger.info("su -c supported")
         except ADBError:
             self._logger.debug("Check for su -c failed")
 
-        # Do we have Android's su?
+        # Check if Android's su 0 command works.
+        # su 0 id will hang on Pixel 2 8.1.0/OPM2.171019.029.B1/4720900
+        # rooted via magisk. If we already have detected su -c support,
+        # we can skip this check.
         try:
-            if self.shell_output("su 0 id", timeout=timeout).find(uid) != -1:
+            if (not self._have_su and
+                self.shell_output("su 0 id", timeout=timeout).find(uid) != -1):
                 self._have_android_su = True
                 self._logger.info("su 0 supported")
         except ADBError:
             self._logger.debug("Check for su 0 failed")
 
         self._mkdir_p = None
         # Force the use of /system/bin/ls or /system/xbin/ls in case
         # there is /sbin/ls which embeds ansi escape codes to colorize
@@ -1810,17 +1816,17 @@ class ADBDevice(ADBCommand):
             local = new_local
             # See do_sync_push in
             # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
             # Work around change in behavior in adb 1.0.36 where if
             # the remote destination directory exists, adb push will
             # copy the source directory *into* the destination
             # directory otherwise it will copy the source directory
             # *onto* the destination directory.
-            if self._adb_version >= '1.0.36':
+            if self._adb_version >= '1.0.36' and self.is_dir(remote):
                 remote = '/'.join(remote.rstrip('/').split('/')[:-1])
         try:
             self.command_output(["push", local, remote], timeout=timeout)
         except BaseException:
             raise
         finally:
             if copy_required:
                 shutil.rmtree(temp_parent)
--- a/testing/mozbase/mozdevice/mozdevice/adb_android.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py
@@ -1,15 +1,16 @@
 # 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/.
 
 from __future__ import absolute_import, print_function
 
 import os
+import posixpath
 import re
 import time
 
 from abc import ABCMeta
 
 from . import version_codes
 
 from .adb import ADBDevice, ADBError, ADBRootError
@@ -93,16 +94,31 @@ class ADBAndroid(ADBDevice):
                 self._logger.info('Setting SELinux Permissive Mode')
                 self.shell_output("setenforce Permissive", timeout=timeout, root=True)
         except (ADBError, ADBRootError) as e:
             self._logger.warning('Unable to set SELinux Permissive due to %s.' % e)
             self.selinux = False
 
         self.version = int(self.shell_output("getprop ro.build.version.sdk",
                                              timeout=timeout))
+        # Beginning in Android 8.1 /data/anr/traces.txt no longer contains
+        # a single file traces.txt but instead will contain individual files
+        # for each stack.
+        # See https://github.com/aosp-mirror/platform_build/commit/
+        # fbba7fe06312241c7eb8c592ec2ac630e4316d55
+        stack_trace_dir = self.shell_output("getprop dalvik.vm.stack-trace-dir",
+                                            timeout=timeout)
+        if not stack_trace_dir:
+            stack_trace_file = self.shell_output("getprop dalvik.vm.stack-trace-file",
+                                                 timeout=timeout)
+            if stack_trace_file:
+                stack_trace_dir = posixpath.dirname(stack_trace_file)
+            else:
+                stack_trace_dir = '/data/anr'
+        self.stack_trace_dir = stack_trace_dir
 
     def reboot(self, timeout=None):
         """Reboots the device.
 
         :param timeout: optional integer specifying the maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -369,16 +369,17 @@ def grant_runtime_permissions(build_obj,
         sdk_level = device.version
         if sdk_level and sdk_level >= 23:
             _log_info("Granting important runtime permissions to %s" % app)
             device.shell_output('pm grant %s android.permission.WRITE_EXTERNAL_STORAGE' % app)
             device.shell_output('pm grant %s android.permission.READ_EXTERNAL_STORAGE' % app)
             device.shell_output('pm grant %s android.permission.ACCESS_COARSE_LOCATION' % app)
             device.shell_output('pm grant %s android.permission.ACCESS_FINE_LOCATION' % app)
             device.shell_output('pm grant %s android.permission.CAMERA' % app)
+            device.shell_output('pm grant %s android.permission.RECORD_AUDIO' % app)
     except Exception:
         _log_warning("Unable to grant runtime permissions to %s" % app)
 
 
 class AndroidEmulator(object):
 
     """
         Support running the Android emulator with an AVD from Mozilla
--- a/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
+++ b/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
@@ -18,17 +18,22 @@ def printstatus(name, returncode):
     Note that mozlog structured action "process_exit" should be used
     instead of that in new code.
     """
     print("TEST-INFO | %s: %s" % (name, strstatus(returncode)))
 
 
 def dump_screen(utilityPath, log):
     """dumps a screenshot of the entire screen to a directory specified by
-    the MOZ_UPLOAD_DIR environment variable"""
+    the MOZ_UPLOAD_DIR environment variable.
+
+    :param utilityPath: Path of utility programs. This is typically a path
+        to either the objdir's bin directory or a path to the host utilities.
+    :param log: Reference to logger.
+    """
 
     is_structured_log = hasattr(log, 'process_exit')
 
     # Need to figure out which OS-dependent tool to use
     if mozinfo.isUnix:
         utility = [os.path.join(utilityPath, "screentopng")]
         utilityname = "screentopng"
     elif mozinfo.isMac:
@@ -56,8 +61,53 @@ def dump_screen(utilityPath, log):
         returncode = subprocess.call(utility + [imgfilename])
         if is_structured_log:
             log.process_exit(utilityname, returncode)
         else:
             printstatus(utilityname, returncode)
     except OSError as err:
         log.info("Failed to start %s for screenshot: %s"
                  % (utility[0], err.strerror))
+
+
+def dump_device_screen(device, log):
+    """dumps a screenshot of a real device's entire screen to a directory
+    specified by the MOZ_UPLOAD_DIR environment variable. Cloned from
+    mozscreenshot.dump_screen.
+
+    :param device: Reference to an ADBAndroid object which provides the
+        interface to interact with Android devices.
+    :param log: Reference to logger.
+    """
+
+    utilityname = 'screencap'
+    is_structured_log = hasattr(log, 'process_exit')
+
+    # Get dir where to write the screenshot file
+    parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
+    if not parent_dir:
+        log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
+        return
+
+    # Run the capture
+    try:
+        # Android 6.0 and later support mktemp.  See
+        # https://android.googlesource.com/platform/system/core/
+        # +/master/shell_and_utilities/README.md#android-6_0-marshmallow
+        # We can use mktemp on real devices since we do not test on
+        # real devices older than Android 6.0. Note we must create the
+        # file without an extension due to limitations in mktemp.
+        filename = device.shell_output('mktemp -p %s mozilla-test-fail-screenshot_XXXXXX' %
+                                       device.test_root)
+        pngfilename = filename + '.png'
+        device.mv(filename, pngfilename)
+        if is_structured_log:
+            log.process_start(utilityname)
+        device.shell_output('%s -p %s' % (utilityname, pngfilename))
+        if is_structured_log:
+            log.process_exit(utilityname, 0)
+        else:
+            printstatus(utilityname, 0)
+        device.pull(pngfilename, parent_dir)
+        device.rm(pngfilename)
+    except Exception as err:
+        log.info("Failed to start %s for screenshot: %s"
+                 % (utilityname, str(err)))
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -328005,16 +328005,22 @@
     ]
    ],
    "css/css-align/self-alignment/place-self-shorthand-006.html": [
     [
      "/css/css-align/self-alignment/place-self-shorthand-006.html",
      {}
     ]
    ],
+   "css/css-animations/AnimationEffect-getComputedTiming.tentative.html": [
+    [
+     "/css/css-animations/AnimationEffect-getComputedTiming.tentative.html",
+     {}
+    ]
+   ],
    "css/css-animations/CSSAnimation-animationName.tentative.html": [
     [
      "/css/css-animations/CSSAnimation-animationName.tentative.html",
      {}
     ]
    ],
    "css/css-animations/CSSAnimation-canceling.tentative.html": [
     [
@@ -328029,22 +328035,16 @@
     ]
    ],
    "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": [
     [
@@ -334183,16 +334183,94 @@
     ]
    ],
    "css/css-transforms/translate-getComputedStyle.html": [
     [
      "/css/css-transforms/translate-getComputedStyle.html",
      {}
     ]
    ],
+   "css/css-transitions/AnimationEffect-getComputedTiming.tentative.html": [
+    [
+     "/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSPseudoElement-getAnimations.tentative.html": [
+    [
+     "/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-canceling.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-canceling.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-currentTime.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-currentTime.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-effect.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-effect.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-finished.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-finished.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-ready.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-ready.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-startTime.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-startTime.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/CSSTransition-transitionProperty.tentative.html": [
+    [
+     "/css/css-transitions/CSSTransition-transitionProperty.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/Document-getAnimations.tentative.html": [
+    [
+     "/css/css-transitions/Document-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/Element-getAnimations.tentative.html": [
+    [
+     "/css/css-transitions/Element-getAnimations.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/KeyframeEffect-getKeyframes.tentative.html": [
+    [
+     "/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html",
+     {}
+    ]
+   ],
+   "css/css-transitions/KeyframeEffect-target.tentative.html": [
+    [
+     "/css/css-transitions/KeyframeEffect-target.tentative.html",
+     {}
+    ]
+   ],
    "css/css-transitions/before-DOMContentLoaded-001.html": [
     [
      "/css/css-transitions/before-DOMContentLoaded-001.html",
      {}
     ]
    ],
    "css/css-transitions/before-load-001.html": [
     [
@@ -334213,16 +334291,22 @@
     ]
    ],
    "css/css-transitions/detached-container-001.html": [
     [
      "/css/css-transitions/detached-container-001.html",
      {}
     ]
    ],
+   "css/css-transitions/event-dispatch.tentative.html": [
+    [
+     "/css/css-transitions/event-dispatch.tentative.html",
+     {}
+    ]
+   ],
    "css/css-transitions/events-001.html": [
     [
      "/css/css-transitions/events-001.html",
      {}
     ]
    ],
    "css/css-transitions/events-002.html": [
     [
@@ -514572,82 +514656,82 @@
   "css/css-align/self-alignment/place-self-shorthand-006.html": [
    "fb5dce1b13428e5887a66b8a2a378cc540d99c47",
    "testharness"
   ],
   "css/css-align/ttwf-reftest-alignContent.html": [
    "d5737057284a603673e588a61997cf89d001cd6f",
    "visual"
   ],
+  "css/css-animations/AnimationEffect-getComputedTiming.tentative.html": [
+   "fd2197fcace453760d8ef7613139a978ab8ba0e6",
+   "testharness"
+  ],
   "css/css-animations/CSSAnimation-animationName.tentative.html": [
-   "49bd0afb4721882aaf434e4469f52a7efbef6b06",
+   "370d5ef85e27c2e83deb54522a31da9deb8b556c",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-canceling.tentative.html": [
-   "a9e165417ee8ddb10d809eec3fcb13c22469e2c8",
+   "b76af2c24ac636bf3ea73050eb64555d6b0e2100",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-effect.tentative.html": [
-   "6597547f8468990da2424de7bde331e361a84c5d",
+   "d288f47c60f5360161bdcc14bf2c1399654caa1a",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-finished.tentative.html": [
-   "0003286de4ab6291dc5d935553569019606b38b9",
-   "testharness"
-  ],
-  "css/css-animations/CSSAnimation-getComputedTiming.tentative.html": [
-   "fb13f7abbdf5af4df4824cd0de8b7f1ff289f972",
+   "57dd0d5020c9d6a4bc30dbe9570bd3eb8cbba9ae",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-getCurrentTime.tentative.html": [
-   "fbe51f6df3fae988956c92b19c278b98475a5e6d",
+   "4a0b9bdce744d9f452f4989b41025bd94931dc4b",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-id.tentative.html": [
-   "79651c2d650896c2420ab7548426de07d98ee053",
+   "623e01d2354aad5dc746558dbc40821d09173472",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-pausing.tentative.html": [
-   "34dd06b0da647548c10a1731049ba195be17eef3",
+   "2b6e6853b4178f90e820a16a74a5d63524123d32",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-playState.tentative.html": [
-   "be95b4fc31cd41c629c15ac4e46e1184d8e4b62c",
+   "acfdc1348fb0acbcb2670cfb55cace589ac93fd4",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-ready.tentative.html": [
-   "99fa0856ce370f67fef56942e8f77a9a884d9cbb",
+   "f70ebd17003988d641a7ce5645d92b1c968dac3e",
    "testharness"
   ],
   "css/css-animations/CSSAnimation-startTime.tentative.html": [
-   "9c8c57b406e11be16e79adcd4fbb96c17c545600",
+   "edef3b239e8a6c2f10b82017a448b0a2dfdd8b9d",
    "testharness"
   ],
   "css/css-animations/CSSPseudoElement-getAnimations.tentative.html": [
-   "f1257fe481fbe6d6dd6cb045e27d8d6b664dd942",
+   "8fcdf3879dc032c615a18898f701a05e10cd2b06",
    "testharness"
   ],
   "css/css-animations/Document-getAnimations.tentative.html": [
-   "1ebf4657fa011b1d92ae17cc2d18e1219197287d",
+   "c11b0a908760f4a365fb6f82e92b98341e94fb4c",
    "testharness"
   ],
   "css/css-animations/Element-getAnimations-dynamic-changes.tentative.html": [
-   "ca6a23c13b0e2552fe60506e4ca9ad5e98b91330",
+   "a5e22884271cf301c728c26eec32fe399d08f0d1",
    "testharness"
   ],
   "css/css-animations/Element-getAnimations.tentative.html": [
-   "ee80cacf15b8741c8cebbcaf46b113fcf20fa308",
+   "23f9ec8e3841535fe4c1c089c7dd6d976a14daab",
    "testharness"
   ],
   "css/css-animations/KeyframeEffect-getKeyframes.tentative.html": [
    "280f32a4852abaeb847092032fa49c9a6cb9797d",
    "testharness"
   ],
   "css/css-animations/KeyframeEffect-target.tentative.html": [
-   "cc066b4ec19fb80c93e531faba754a07a98efe73",
+   "4991762099d4fac96f56a8839651bd8368cef11c",
    "testharness"
   ],
   "css/css-animations/META.yml": [
    "3ef19970007d4bb6a889f9601bc7c910f619f841",
    "support"
   ],
   "css/css-animations/animation-common-ref.html": [
    "ddc7da67ddf5fed83e653d1130a65f5c1e3a6dec",
@@ -514949,21 +515033,21 @@
    "77f514a297bfd271abb488f086723af7ed0c9ff6",
    "testharness"
   ],
   "css/css-animations/animationstart-and-animationend-events-manual.html": [
    "4ca5eb736e76274eee9883121970fee0ab8280ca",
    "manual"
   ],
   "css/css-animations/event-dispatch.tentative.html": [
-   "6211cbff0e182dc2f9c8dd3828868d5ef6218b24",
+   "54bc9499a535dba81f302e4c40eb20193bee0da6",
    "testharness"
   ],
   "css/css-animations/event-order.tentative.html": [
-   "91d3851c015e10512b99c6b3038dc3873db1aaf5",
+   "93d452ace07163b10af18245d6799d9823448e1e",
    "testharness"
   ],
   "css/css-animations/idlharness.html": [
    "1d3ed2b9b806792c7efaeeee9ab264101dd222bc",
    "testharness"
   ],
   "css/css-animations/pending-style-changes-001.html": [
    "fb74d7fa7d062d60153d47913df9eb2b0c7267c8",
@@ -554376,16 +554460,68 @@
   "css/css-transforms/ttwf-transform-translatex-001.html": [
    "f85d332400f35fdc6e2f81dfbdebfb470b6dfc6b",
    "reftest"
   ],
   "css/css-transforms/ttwf-transform-translatey-001.html": [
    "c8caa6bd7c17de28dcd3f50c6357adc574084225",
    "reftest"
   ],
+  "css/css-transitions/AnimationEffect-getComputedTiming.tentative.html": [
+   "4b6a28b5694f52103bcb6b383dbcd1ad6553cf3a",
+   "testharness"
+  ],
+  "css/css-transitions/CSSPseudoElement-getAnimations.tentative.html": [
+   "5229881e30fa903da8afc76b3664c7c4483add53",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-canceling.tentative.html": [
+   "72b1dbff436fd12574beece0dd295ca3d779b73c",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-currentTime.tentative.html": [
+   "4c7a7a4891636a7201f07b671ff605dca3ddcc2b",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-effect.tentative.html": [
+   "3979dd29533ad3a3af7135aaa5255721aa100702",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-finished.tentative.html": [
+   "23750e7a98eaf7675386850c711b5b7738fccc01",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-ready.tentative.html": [
+   "92aaa490591b9eb3e2db55438d955a9811cd7904",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-startTime.tentative.html": [
+   "7c74ed2037ab4e8242d465d76a4cbd360abee4a2",
+   "testharness"
+  ],
+  "css/css-transitions/CSSTransition-transitionProperty.tentative.html": [
+   "8eb284107d350fb2a5da1b176aa213f807cd212f",
+   "testharness"
+  ],
+  "css/css-transitions/Document-getAnimations.tentative.html": [
+   "bffc05f205fbc80ee9d27ee4ca8d3fefe00ffd63",
+   "testharness"
+  ],
+  "css/css-transitions/Element-getAnimations.tentative.html": [
+   "26e988ffe69759ab3b9216d24af2a28a70e0c08b",
+   "testharness"
+  ],
+  "css/css-transitions/KeyframeEffect-getKeyframes.tentative.html": [
+   "08a90734392c2e14c0b2593144796e713ff0a20d",
+   "testharness"
+  ],
+  "css/css-transitions/KeyframeEffect-target.tentative.html": [
+   "dbb2a43f784db98317c4d37b32a376f6c27213ce",
+   "testharness"
+  ],
   "css/css-transitions/META.yml": [
    "581515c493e853d0c4a3c75b340f3bb5785cc635",
    "support"
   ],
   "css/css-transitions/README.md": [
    "e44decfea26e9116e04f25db34eca7eb6bcee8ac",
    "support"
   ],
@@ -554404,16 +554540,20 @@
   "css/css-transitions/currentcolor-animation-001.html": [
    "e36e748150680628c150a12f686579da6e99ddcc",
    "testharness"
   ],
   "css/css-transitions/detached-container-001.html": [
    "efbc34f77740dce56a29871ecccfc20240381ad5",
    "testharness"
   ],
+  "css/css-transitions/event-dispatch.tentative.html": [
+   "81bba60a66e7eb9800113bcd04dcc47a15002fb0",
+   "testharness"
+  ],
   "css/css-transitions/events-001.html": [
    "59d2011b39780def3de6f6899a51c6de2068bf4c",
    "testharness"
   ],
   "css/css-transitions/events-002.html": [
    "3ec2cc3da475c70afa1187a04a649d3c3f7f50fb",
    "testharness"
   ],
@@ -554545,17 +554685,17 @@
    "85dd7324815b8f8ef1a1d0496224c1a0661db9d8",
    "support"
   ],
   "css/css-transitions/support/generalParallelTest.js": [
    "f6e14128fc07f08984ac705b2f6ec03f02bc2215",
    "support"
   ],
   "css/css-transitions/support/helper.js": [
-   "f11e4347abee6ee3cee07e2ee9ec76907c9ab555",
+   "2e7ecff2ae7e60a3250cc787cbf888f1881bfc27",
    "support"
   ],
   "css/css-transitions/support/import-green.css": [
    "537104e663364492c6ef388e4afce190e9c5bc58",
    "support"
   ],
   "css/css-transitions/support/import-red.css": [
    "9945ef47114c2841a746c99a2fb1e93e050aac8b",
rename from testing/web-platform/tests/css/css-animations/CSSAnimation-getComputedTiming.tentative.html
rename to testing/web-platform/tests/css/css-animations/AnimationEffect-getComputedTiming.tentative.html
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-getComputedTiming.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/AnimationEffect-getComputedTiming.tentative.html
@@ -1,11 +1,11 @@
 <!doctype html>
 <meta charset=utf-8>
-<title>CSSAnimation.getComputedTiming()</title>
+<title>AnimationEffect.getComputedTiming() 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/#cssanimation">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support/testcommon.js"></script>
 <style>
 @keyframes moveAnimation {
   from { margin-left: 100px }
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-animationName.tentative.html
@@ -6,17 +6,16 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support/testcommon.js"></script>
 <style>
 @keyframes xyz {
   to { left: 100px }
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t);
   div.style.animation = 'xyz 100s';
   assert_equals(div.getAnimations()[0].animationName, 'xyz',
@@ -34,9 +33,8 @@ 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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-canceling.tentative.html
@@ -13,17 +13,16 @@
 @keyframes marginLeftAnim {
   to { margin-left: 100px }
 }
 @keyframes marginLeftAnim100To200 {
   from { margin-left: 100px }
   to { margin-left: 200px }
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 promise_test(async t => {
   const div = addDiv(t, { style: 'animation: translateAnim 100s' });
   const animation = div.getAnimations()[0];
 
@@ -190,10 +189,8 @@ promise_test(async t => {
   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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-effect.tentative.html
@@ -11,17 +11,16 @@
   from {
     margin-left: 0px;
   }
   to {
     margin-left: 100px;
   }
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 promise_test(async t => {
   const div = addDiv(t);
   div.style.animation = 'anim 100s';
 
@@ -124,9 +123,8 @@ promise_test(async t => {
   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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-finished.tentative.html
@@ -7,17 +7,16 @@
 <script src="/resources/testharnessreport.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(async t => {
@@ -79,9 +78,8 @@ promise_test(async t => {
 
   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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-getCurrentTime.tentative.html
@@ -1,34 +1,30 @@
 <!doctype html>
-<html>
-<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">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/testcommon.js"></script>
 <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="support/testcommon.js"></script>
-</head>
-<body>
 <div id="log"></div>
 <script type="text/javascript">
 
 'use strict';
 
 promise_test(async t => {
   const div = addDiv(t, { class: 'animated-div' });
   div.style.animation = 'anim 100s';
@@ -65,10 +61,8 @@ promise_test(async t => {
     () => {
       animation.currentTime = null;
     },
     'Expect TypeError exception on trying to set Animation.currentTime to null'
   );
 }, 'Setting currentTime to null on a CSS animation throws');
 
 </script>
-</body>
-</html>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-id.tentative.html
@@ -4,26 +4,24 @@
 <!-- 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="support/testcommon.js"></script>
 <style>
 @keyframes abc { }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t);
   div.style.animation = 'abc 100s';
   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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-pausing.tentative.html
@@ -7,17 +7,16 @@
 <script src="/resources/testharnessreport.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';
 
 const getMarginLeft = cs => parseFloat(cs.marginLeft);
 
 promise_test(async t => {
   const div = addDiv(t);
@@ -164,9 +163,8 @@ promise_test(async t => {
                 '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.
   await animation.ready;
 }, 'Setting the current time completes a pending pause');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-playState.tentative.html
@@ -4,17 +4,16 @@
 <!-- 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="support/testcommon.js"></script>
 <style>
 @keyframes anim { }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t, { 'style': 'animation: anim 100s' });
   const animation = div.getAnimations()[0];
   assert_true(animation.pending);
@@ -49,9 +48,8 @@ test(t => {
 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 canceled');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-ready.tentative.html
@@ -6,17 +6,16 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.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(async t => {
   const div = addDiv(t);
   div.style.animation = 'abc 100s paused';
   const animation = div.getAnimations()[0];
@@ -92,9 +91,8 @@ promise_test(async t => {
   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>
--- a/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSAnimation-startTime.tentative.html
@@ -1,34 +1,30 @@
 <!doctype html>
-<html>
-<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">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/testcommon.js"></script>
 <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="support/testcommon.js"></script>
-</head>
-<body>
 <div id="log"></div>
 <script type="text/javascript">
 
 'use strict';
 
 test(t => {
   const div = addDiv(t, { 'class': 'animated-div' });
   div.style.animation = 'anim 100s 100s';
@@ -66,10 +62,8 @@ promise_test(async t => {
   animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
   await eventWatcher.wait_for('animationstart');
 
   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');
 
 </script>
-</body>
-</html>
--- a/testing/web-platform/tests/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/CSSPseudoElement-getAnimations.tentative.html
@@ -21,17 +21,16 @@
   transition: all 100s;
 }
 .after-change::after {
   width: 100px;
   height: 100px;
   content: '';
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t, { class: 'before' });
   const pseudoTarget = document.getAnimations()[0].effect.target;
   assert_equals(pseudoTarget.getAnimations().length, 1,
@@ -69,9 +68,8 @@ test(t => {
   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 ' +
    'animations in the expected order');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
@@ -20,17 +20,16 @@
 }
 ::before {
   content: ''
 }
 ::after {
   content: ''
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
     + ' with no animations');
@@ -278,9 +277,8 @@ test(t => {
   assert_equals(anims[2].effect.target.type, '::after',
                 'The animation targeting the ::after element comes third');
   assert_equals(anims[3].effect.target, child,
                 'The animation targeting the child element comes last');
 }, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
    'after sorting');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations-dynamic-changes.tentative.html
@@ -9,17 +9,16 @@ Element.getAnimations() - Dynamic change
 <script src="/resources/testharnessreport.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(async t => {
   const div = addDiv(t);
   div.style.animation = 'anim1 100s';
   const originalAnimation = div.getAnimations()[0];
@@ -157,9 +156,8 @@ promise_test(async t => {
   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>
--- a/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/Element-getAnimations.tentative.html
@@ -19,17 +19,16 @@
 ::before {
   content: ''
 }
 ::after {
   content: ''
 }
 @keyframes empty { }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t);
   assert_equals(div.getAnimations().length, 0,
     'getAnimations returns an empty sequence for an element'
@@ -441,9 +440,8 @@ test(t => {
   assert_equals(animations[4].effect.target, child2,
                 'The animation targeting the child2 element ' +
                 'should be returned last');
 
 }, '{ subtree: true } on an element with many descendants returns animations'
    + ' from all the descendants');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/KeyframeEffect-target.tentative.html
@@ -10,17 +10,16 @@
 @keyframes anim { }
 ::before {
   content: ''
 }
 ::after {
   content: ''
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   const div = addDiv(t);
   div.style.animation = 'anim 100s';
   const animation = div.getAnimations()[0];
@@ -57,9 +56,8 @@ test(t => {
                 'the same as the one from the argument of ' +
                 'KeyframeEffect constructor');
   assert_equals(anims[0].effect.target, newAnim.effect.target,
                 'Both animations return the same target object');
 }, 'effect.target from the script-generated animation should return the same ' +
    'CSSPseudoElement object as that from the CSS generated animation');
 
 </script>
-</body>
--- a/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/event-dispatch.tentative.html
@@ -6,17 +6,16 @@
 <script src="/resources/testharnessreport.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>
 <script>
 'use strict';
 
 const setupAnimation = (t, animationStyle) => {
   const div = addDiv(t, { style: 'animation: ' + animationStyle });
   const watcher = new EventWatcher(t, div, [ 'animationstart',
                                              'animationiteration',
@@ -414,10 +413,8 @@ promise_test(async t => {
   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>
--- a/testing/web-platform/tests/css/css-animations/event-order.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/event-order.tentative.html
@@ -6,17 +6,16 @@
 <script src="/resources/testharnessreport.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>
 <script type='text/javascript'>
 'use strict';
 
 /**
  * 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
@@ -155,10 +154,8 @@ promise_test(async t => {
 
   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>
rename from dom/animation/test/css-transitions/test_animation-computed-timing.html
rename to testing/web-platform/tests/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-computed-timing.html
+++ b/testing/web-platform/tests/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html
@@ -1,317 +1,329 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>AnimationEffect.getComputedTiming() for CSS transitions</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/helper.js"></script>
 <style>
 
 .animated-div {
   margin-left: 100px;
 }
 
 </style>
-<body>
 <div id="log"></div>
 <script>
 
 'use strict';
 
+
 // --------------------
 // delay
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-
-  var effect = div.getAnimations()[0].effect;
-  assert_equals(effect.getComputedTiming().delay, 0,
-                'Initial value of delay');
+  const effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 0, 'Initial value of delay');
 }, 'delay of a new tranisition');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().delay, 10000,
                 'Initial value of delay');
 }, 'Positive delay of a new transition');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s -5s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().delay, -5000,
                 'Initial value of delay');
 }, 'Negative delay of a new transition');
 
 
 // --------------------
 // endDelay
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().endDelay, 0,
                 'Initial value of endDelay');
 }, 'endDelay of a new transition');
 
 
 // --------------------
 // fill
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
-  assert_equals(effect.getComputedTiming().fill, 'backwards',
-                'Fill backwards');
+  const effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().fill, 'backwards', 'Fill backwards');
 }, 'fill of a new transition');
 
 
 // --------------------
 // iterationStart
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterationStart, 0,
                 'Initial value of iterationStart');
 }, 'iterationStart of a new transition');
 
 
 // --------------------
 // iterations
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().iterations, 1,
                 'Initial value of iterations');
 }, 'iterations of a new transition');
 
 
 // --------------------
 // duration
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().duration, 10000,
                 'Initial value of duration');
 }, 'duration of a new transition');
 
 
 // --------------------
 // direction
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class : 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().direction, 'normal',
                 'Initial value of direction');
 }, 'direction of a new transition');
 
 
 // --------------------
 // easing
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().easing, 'linear',
                 'Initial value of easing');
 }, 'easing of a new transition');
 
 
 // ------------------------------
 // endTime
 // = max(start delay + active duration + end delay, 0)
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 100s -5s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
-  var answer = 100000 - 5000; // ms
+  const effect = div.getAnimations()[0].effect;
+  const answer = 100000 - 5000; // ms
   assert_equals(effect.getComputedTiming().endTime, answer,
                 'Initial value of endTime');
 }, 'endTime of a new transition');
 
 
 // --------------------
 // activeDuration
 // = iteration duration * iteration count(==1)
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 100s -5s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().activeDuration, 100000,
                 'Initial value of activeDuration');
 }, 'activeDuration of a new transition');
 
 
 // --------------------
 // localTime
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().localTime, 0,
                 'Initial value of localTime');
 }, 'localTime of a new transition');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var anim = div.getAnimations()[0];
+  const anim = div.getAnimations()[0];
   anim.currentTime = 5000;
   assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
                 'current localTime after setting currentTime');
 }, 'localTime is always equal to currentTime');
 
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+promise_test(async t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var anim = div.getAnimations()[0];
+  const anim = div.getAnimations()[0];
   anim.playbackRate = 2; // 2 times faster
 
-  anim.ready.then(t.step_func(function() {
-    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
-                  'localTime is equal to currentTime');
-    return waitForFrame();
-  })).then(t.step_func_done(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');
 
 
 // --------------------
 // progress
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10.5s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().progress, 0.0,
                 'Initial value of progress');
 }, 'progress of a new transition');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10.5s 2s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().progress, 0.0,
                 'Initial value of progress');
 }, 'progress of a new transition with positive delay in before phase');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10.5s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var anim = div.getAnimations()[0];
-  anim.finish()
+  const anim = div.getAnimations()[0];
+  anim.finish();
   assert_equals(anim.effect.getComputedTiming().progress, null,
                 'finished progress');
 }, 'progress of a finished transition');
 
 
 // --------------------
 // currentIteration
 // --------------------
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
 }, 'currentIteration of a new transition');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s 2s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var effect = div.getAnimations()[0].effect;
+  const effect = div.getAnimations()[0].effect;
   assert_equals(effect.getComputedTiming().currentIteration, 0,
                 'Initial value of currentIteration');
 }, 'currentIteration of a new transition with positive delay in before phase');
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
   div.style.transition = 'margin-left 10s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   div.style.marginLeft = '10px';
 
-  var anim = div.getAnimations()[0];
+  const anim = div.getAnimations()[0];
   anim.finish();
   assert_equals(anim.effect.getComputedTiming().currentIteration, null,
                 'finished currentIteration');
 }, 'currentIteration of a finished transition');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
rename to testing/web-platform/tests/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html
--- a/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSPseudoElement-getAnimations.tentative.html
@@ -1,47 +1,47 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSPseudoElement.getAnimations() for CSS transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#animation-composite-order">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/helper.js"></script>
 <style>
 .init::before {
   content: '';
   height: 0px;
   width: 0px;
   opacity: 0;
   transition: all 100s;
 }
 .change::before {
   height: 100px;
   width: 100px;
   opacity: 1;
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t, { class: 'init' });
-  flushComputedStyle(div);
+test(t => {
+  const div = addDiv(t, { class: 'init' });
+  getComputedStyle(div).width;
   div.classList.add('change');
 
   // Sanity checks
   assert_equals(document.getAnimations().length, 3,
                 'Got expected number of animations on document');
-  var pseudoTarget = document.getAnimations()[0].effect.target;
+  const pseudoTarget = document.getAnimations()[0].effect.target;
   assert_class_string(pseudoTarget, 'CSSPseudoElement',
                       'Got pseudo-element target');
 
   // Check animations returned from the pseudo element are in correct order
-  var anims = pseudoTarget.getAnimations();
+  const anims = pseudoTarget.getAnimations();
   assert_equals(anims.length, 3,
                 'Got expected number of animations on pseudo-element');
   assert_equals(anims[0].transitionProperty, 'height');
   assert_equals(anims[1].transitionProperty, 'opacity');
   assert_equals(anims[2].transitionProperty, 'width');
 }, 'getAnimations sorts simultaneous transitions by name');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_animation-cancel.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-canceling.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-cancel.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-canceling.tentative.html
@@ -1,235 +1,252 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Canceling a CSS transition</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions/#starting">
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(waitForFrame).then(function() {
-    assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
-                      'transform style is animated before cancelling');
-    transition.cancel();
-    assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
-                  'transform style is no longer animated after cancelling');
-  });
-}, 'Animated style is cleared after cancelling a running CSS transition');
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+  await waitForFrame();
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+  assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
+                    'transform style is animated before canceling');
+  transition.cancel();
+  assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
+                'transform style is no longer animated after canceling');
+}, 'Animated style is cleared after canceling a running CSS transition');
+
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    transition.cancel();
-    assert_equals(getComputedStyle(div).marginLeft, '1000px',
-                  'margin-left style is not animated after cancelling');
-    transition.play();
-    assert_equals(getComputedStyle(div).marginLeft, '0px',
-                  'margin-left style is animated after re-starting transition');
-    return transition.ready;
-  }).then(function() {
-    assert_equals(transition.playState, 'running',
-                  'Transition succeeds in running after being re-started');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  transition.cancel();
+  assert_equals(getComputedStyle(div).marginLeft, '1000px',
+                'margin-left style is not animated after canceling');
+  transition.play();
+  assert_equals(getComputedStyle(div).marginLeft, '0px',
+                'margin-left style is animated after re-starting transition');
+
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running',
+                'Transition succeeds in running after being re-started');
 }, 'After canceling a transition, it can still be re-used');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    transition.finish();
-    transition.cancel();
-    assert_equals(getComputedStyle(div).marginLeft, '1000px',
-                  'margin-left style is not animated after cancelling');
-    transition.play();
-    assert_equals(getComputedStyle(div).marginLeft, '0px',
-                  'margin-left style is animated after re-starting transition');
-    return transition.ready;
-  }).then(function() {
-    assert_equals(transition.playState, 'running',
-                  'Transition succeeds in running after being re-started');
-  });
-}, 'After cancelling a finished transition, it can still be re-used');
+  const transition = div.getAnimations()[0];
+  await transition.ready;
 
-test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+  transition.finish();
+  transition.cancel();
+  assert_equals(getComputedStyle(div).marginLeft, '1000px',
+                'margin-left style is not animated after canceling');
+  transition.play();
+  assert_equals(getComputedStyle(div).marginLeft, '0px',
+                'margin-left style is animated after re-starting transition');
+
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running',
+                'Transition succeeds in running after being re-started');
+}, 'After canceling a finished transition, it can still be re-used');
+
+test(t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
+  const transition = div.getAnimations()[0];
   transition.cancel();
   assert_equals(getComputedStyle(div).marginLeft, '1000px',
-                'margin-left style is not animated after cancelling');
+                'margin-left style is not animated after canceling');
 
   // Trigger a change to a transition property and check that this
   // doesn't cause the animation to become live again
   div.style.transitionDuration = '200s';
-  flushComputedStyle(div);
+  getComputedStyle(div).marginLeft;
   assert_equals(getComputedStyle(div).marginLeft, '1000px',
                 'margin-left style is still not animated after updating'
                 + ' transition-duration');
   assert_equals(transition.playState, 'idle',
                 'Transition is still idle after updating transition-duration');
-}, 'After cancelling a transition, updating transition properties doesn\'t make'
+}, 'After canceling a transition, updating transition properties doesn\'t make'
    + ' it live again');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    div.style.display = 'none';
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-    assert_equals(getComputedStyle(div).marginLeft, '1000px');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  div.style.display = 'none';
+
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
+  assert_equals(getComputedStyle(div).marginLeft, '1000px');
 }, 'Setting display:none on an element cancels its transitions');
 
-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', 'margin-left: 0px');
 
-  flushComputedStyle(childDiv);
+  getComputedStyle(childDiv).marginLeft;
 
   childDiv.style.transition = 'margin-left 100s';
   childDiv.style.marginLeft = '1000px';
 
-  var transition = childDiv.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    parentDiv.style.display = 'none';
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-    assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
-  });
+  const transition = childDiv.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  parentDiv.style.display = 'none';
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
+  assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
 }, 'Setting display:none cancels transitions on a child element');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    // Set an unrecognized property value
-    div.style.transitionProperty = 'none';
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-    assert_equals(getComputedStyle(div).marginLeft, '1000px');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  // Set an unrecognized property value
+  div.style.transitionProperty = 'none';
+  getComputedStyle(div).marginLeft;
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
+  assert_equals(getComputedStyle(div).marginLeft, '1000px');
 }, 'Removing a property from transition-property cancels transitions on that '+
    'property');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    div.style.transition = 'margin-top 10s -10s'; // combined duration is zero
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-    assert_equals(getComputedStyle(div).marginLeft, '1000px');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  div.style.transition = 'margin-left 10s -10s'; // combined duration is zero
+
+  // Note that simply setting the combined duration to zero is not enough to
+  // cancel the transition. We must also update the end value so that the,
+  // "the end value of the running transition is not equal to the value of the
+  // property in the after-change style" condition is true.
+  //
+  // (And note that the zero combined duration is not strictly necessary to
+  // cancel the original transition--but it does ensure another transition is
+  // not generated in its place.)
+
+  div.style.marginLeft = '2000px';
+  getComputedStyle(div).marginLeft;
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
+  assert_equals(getComputedStyle(div).marginLeft, '2000px');
 }, 'Setting zero combined duration');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    div.style.marginLeft = '2000px';
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  div.style.marginLeft = '2000px';
+  getComputedStyle(div).marginLeft;
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
 }, 'Changing style to another interpolable value cancels the original ' +
    'transition');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    div.style.marginLeft = 'auto';
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(div.getAnimations().length, 0,
-                  'There should be no transitions');
-    assert_equals(transition.playState, 'idle');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  div.style.marginLeft = 'auto';
+  getComputedStyle(div).marginLeft;
+  await waitForFrame();
+
+  assert_equals(div.getAnimations().length, 0,
+                'There should be no transitions');
+  assert_equals(transition.playState, 'idle');
 }, 'An after-change style value can\'t be interpolated');
 
-promise_test(function(t) {
-  var div = addDiv(t, { style: 'margin-left: 0px' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'margin-left: 0px' });
+  getComputedStyle(div).marginLeft;
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    assert_equals(transition.playState, 'running');
-    div.style.marginLeft = '0px';
-    flushComputedStyle(div);
-    return waitForFrame();
-  }).then(function() {
-    assert_equals(transition.playState, 'idle');
-  });
+  const transition = div.getAnimations()[0];
+  await transition.ready;
+
+  assert_equals(transition.playState, 'running');
+  div.style.marginLeft = '0px';
+  getComputedStyle(div).marginLeft;
+  await waitForFrame();
+
+  assert_equals(transition.playState, 'idle');
 }, 'Reversing a running transition cancels the original transition');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_animation-currenttime.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-currentTime.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-currenttime.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-currentTime.tentative.html
@@ -1,309 +1,110 @@
 <!doctype html>
-<html>
-  <head>
-    <meta charset=utf-8>
-    <title>Tests for the effect of setting a CSS transition's
-           Animation.currentTime</title>
-    <style>
-
-.animated-div {
-  margin-left: 100px;
-  transition: margin-left 1000s linear 1000s;
-}
-
-    </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">
+<meta charset=utf-8>
+<title>CSSTransition.currentTime</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.js"></script>
+<div id="log"></div>
+<script>
 
 'use strict';
 
-// 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 ANIM_DELAY_MS = 1000000; // 1000s
-const ANIM_DUR_MS = 1000000; // 1000s
+const marginLeft = div => parseFloat(getComputedStyle(div).marginLeft);
 
-/**
- * These helpers get the value that the currentTime needs to be set to, to put
- * an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
- * the middle of various phases or points through the active duration.
- */
-function currentTimeForBeforePhase() {
-  return ANIM_DELAY_MS / 2;
-}
-function currentTimeForActivePhase() {
-  return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
-}
-function currentTimeForAfterPhase() {
-  return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
-}
-function currentTimeForStartOfActiveInterval() {
-  return ANIM_DELAY_MS;
-}
-function currentTimeForFiftyPercentThroughActiveInterval() {
-  return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
-}
-function currentTimeForEndOfActiveInterval() {
-  return ANIM_DELAY_MS + ANIM_DUR_MS;
-}
-
-
-// Expected computed 'margin-left' values at points during the active interval:
-// When we assert_between_inclusive using these values we could in theory cause
-// intermittent failure due to very long delays between paints, but since the
-// active duration is 1000s long, a delay would need to be around 100s to cause
-// that. If that's happening then there are likely other issues that should be
-// fixed, so a failure to make us look into that seems like a good thing.
-const INITIAL_POSITION = 100;
-const TEN_PCT_POSITION = 110;
-const FIFTY_PCT_POSITION = 150;
-const END_POSITION = 200;
-
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
 
-// The terms used for the naming of the following helper functions refer to
-// terms used in the Web Animations specification for specific phases of an
-// animation. The terms can be found here:
-//
-//   http://drafts.csswg.org/web-animations/#animation-effect-phases-and-states
-
-// Called when currentTime is set to zero (the beginning of the start delay).
-function checkStateOnSettingCurrentTimeToZero(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
+  const animation = div.getAnimations()[0];
+  assert_equals(
+    animation.currentTime,
+    0,
+    'currentTime should initially be zero'
+  );
 
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the start delay');
-
-  assert_equals(animation.effect.target.style.animationPlayState, 'running',
-    'Animation.effect.target.style.animationPlayState should be ' +
-    '"running" at the start of the start delay');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, UNANIMATED_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'at the beginning of the start delay');
-}
+  await animation.ready;
 
-// Called when the ready Promise's callbacks should happen
-function checkStateOnReadyPromiseResolved(animation)
-{
-  // 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');
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" on the first paint ' +
-    'tick after animation creation');
+  const seekTime = 150 * MS_PER_SEC;
+  animation.currentTime = seekTime;
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, INITIAL_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'by an animation with a delay on ready Promise resolve');
-}
-
-// Called when currentTime is set to the time the active interval starts.
-function checkStateAtActiveIntervalStartTime(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the active interval');
+  assert_time_equals_literal(
+    animation.currentTime,
+    seekTime,
+    'currentTime is updated'
+  );
+  assert_equals(getComputedStyle(div).marginLeft, '150px');
+}, 'currentTime can be used to seek a CSS transition');
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
-    'the computed value of margin-left should be close to the value at the ' +
-    'beginning of the animation');
-}
-
-function checkStateAtFiftyPctOfActiveInterval(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, FIFTY_PCT_POSITION,
-    'the computed value of margin-left should be half way through the ' +
-    'animation at the midpoint of the active interval');
-}
-
-// Called when currentTime is set to the time the active interval ends.
-function checkStateAtActiveIntervalEndTime(animation)
-{
-  // We don't test animation.currentTime since our caller just set it.
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  const eventWatcher = new EventWatcher(t, div, 'transitionend');
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
 
-  assert_equals(animation.playState, 'finished',
-    'Animation.playState should be "finished" at the end of ' +
-    'the active interval');
-
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, END_POSITION,
-    'the computed value of margin-left should be the final transitioned-to ' +
-    'value at the end of the active duration');
-}
+  const animation = div.getAnimations()[0];
+  await animation.ready;
 
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
+  const marginLeft = () => parseFloat(getComputedStyle(div).marginLeft);
+  assert_equals(marginLeft(div), 100);
 
-  var animation = div.getAnimations()[0];
-  assert_equals(animation.currentTime, 0, 'currentTime should be zero');
-}, 'currentTime of a newly created transition is zero');
-
-
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
+  animation.currentTime = 100 * MS_PER_SEC;
+  assert_equals(marginLeft(div), 100);
 
-  var animation = div.getAnimations()[0];
-
-  // So that animation is running instead of paused when we set currentTime:
-  animation.startTime = animation.timeline.currentTime;
-
-  animation.currentTime = 10;
-  assert_equals(animation.currentTime, 10,
-    'Check setting of currentTime actually works');
-}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
-   'currentTime');
-
+  animation.currentTime = 150 * MS_PER_SEC;
+  assert_equals(marginLeft(div), 150);
 
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, 'transitionend');
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-
-  animation.ready.then(t.step_func(function() {
-    checkStateOnReadyPromiseResolved(animation);
+  animation.currentTime = 200 * MS_PER_SEC;
+  await eventWatcher.wait_for('transitionend');
+  assert_equals(marginLeft(div), 200);
+}, 'Skipping forwards through transition');
 
-    animation.currentTime = currentTimeForStartOfActiveInterval();
-    checkStateAtActiveIntervalStartTime(animation);
-
-    animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
-    checkStateAtFiftyPctOfActiveInterval(animation);
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  const eventWatcher = new EventWatcher(t, div, 'transitionend');
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
 
-    animation.currentTime = currentTimeForEndOfActiveInterval();
-    return eventWatcher.wait_for('transitionend');
-  })).then(t.step_func(function() {
-    checkStateAtActiveIntervalEndTime(animation);
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(function() {
-    t.done();
-  });
-}, 'Skipping forward through transition');
-
-
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, 'transitionend');
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
+  await animation.ready;
 
   // Unlike in the case of CSS animations, we cannot skip to the end and skip
   // backwards since when we reach the end the transition effect is removed and
   // changes to the Animation object no longer affect the element. For
   // this reason we only skip forwards as far as the 50% through point.
 
-  animation.ready.then(t.step_func(function() {
-    animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
-    checkStateAtFiftyPctOfActiveInterval(animation);
-
-    animation.currentTime = currentTimeForStartOfActiveInterval();
+  animation.currentTime = 150 * MS_PER_SEC;
+  assert_equals(marginLeft(div), 150);
 
-    // Despite going backwards from being in the active interval to being
-    // before it, we now expect a 'transitionend' event because the transition
-    // should go from being active to inactive.
-    //
-    // Calling checkStateAtActiveIntervalStartTime will check computed style,
-    // causing computed style to be updated and the 'transitionend' event to
-    // be dispatched synchronously. We need to call wait_for first
-    // otherwise eventWatcher will assert that the event was unexpected.
-    eventWatcher.wait_for('transitionend').then(function() {
-      t.done();
-    });
-    checkStateAtActiveIntervalStartTime(animation);
-  }));
+  animation.currentTime = 100 * MS_PER_SEC;
+  assert_equals(marginLeft(div), 100);
 }, 'Skipping backwards through transition');
 
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
-  animation.ready.then(t.step_func(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');
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(function() {
-    t.done();
-  });
-}, 'Setting currentTime to null');
-
+  await animation.ready;
 
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-  var pauseTime;
+  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 transition throws');
 
-  animation.ready.then(t.step_func(function() {
-    assert_not_equals(animation.currentTime, null,
-      'Animation.currentTime not null on ready Promise resolve');
-    animation.pause();
-    return animation.ready;
-  })).then(t.step_func(function() {
-    pauseTime = animation.currentTime;
-    return waitForFrame();
-  })).then(t.step_func(function() {
-    assert_equals(animation.currentTime, pauseTime,
-      'Animation.currentTime is unchanged after pausing');
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(function() {
-    t.done();
-  });
-}, 'Animation.currentTime after pausing');
-
-    </script>
-  </body>
-</html>
+</script>
rename from dom/animation/test/css-transitions/test_setting-effect.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-effect.tentative.html
--- a/dom/animation/test/css-transitions/test_setting-effect.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-effect.tentative.html
@@ -1,102 +1,122 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSTransition.effect</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src='../testcommon.js'></script>
-<body>
+<script src='support/helper.js'></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var watcher = new EventWatcher(t, div, [ 'transitionend',
-                                           'transitioncancel' ]);
+test(t => {
+  const div = addDiv(t);
+  div.style.left = '0px';
+
+  div.style.transition = 'left 100s';
+  getComputedStyle(div).left;
+  div.style.left = '100px';
+
+  const transition = div.getAnimations()[0];
+
+  transition.effect = null;
+
+  assert_equals(transition.transitionProperty, 'left');
+}, 'After setting a transition\'s effect to null, it still reports the'
+   + ' original transition property');
+
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.left = '0px';
 
   div.style.transition = 'left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).left;
   div.style.left = '100px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    transition.currentTime = 50 * MS_PER_SEC;
-    transition.effect = null;
-    assert_equals(transition.transitionProperty, 'left');
-    assert_equals(transition.playState, 'finished');
-    assert_equals(getComputedStyle(div).left, '100px');
-    return watcher.wait_for('transitionend');
-  });
-}, 'Test for removing a transition effect');
+  const transition = div.getAnimations()[0];
+  await transition.ready;
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var watcher = new EventWatcher(t, div, [ 'transitionend',
-                                           'transitioncancel' ]);
+  transition.effect = null;
+  assert_equals(transition.playState, 'finished');
+}, 'After setting a transition\'s effect to null, it becomes finished');
+
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.left = '0px';
 
   div.style.transition = 'left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).left;
   div.style.left = '100px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    transition.currentTime = 50 * MS_PER_SEC;
-    transition.effect = new KeyframeEffect(div,
-                                           { marginLeft: [ '0px' , '100px'] },
-                                           100 * MS_PER_SEC);
-    assert_equals(transition.transitionProperty, 'left');
-    assert_equals(transition.playState, 'running');
-    assert_equals(getComputedStyle(div).left, '100px');
-    assert_equals(getComputedStyle(div).marginLeft, '50px');
-  });
-}, 'Test for replacing the transition effect by a new keyframe effect');
+  const transition = div.getAnimations()[0];
+  await transition.ready;
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var watcher = new EventWatcher(t, div, [ 'transitionend',
-                                           'transitioncancel' ]);
+  transition.effect = null;
+  assert_equals(getComputedStyle(div).left, '100px');
+}, 'After setting a transition\'s effect to null, style is updated');
+
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.left = '0px';
   div.style.width = '0px';
 
   div.style.transition = 'left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).left;
   div.style.left = '100px';
 
-  var transition = div.getAnimations()[0];
-  return transition.ready.then(function() {
-    transition.currentTime = 50 * MS_PER_SEC;
-    transition.effect = new KeyframeEffect(div,
-                                           { marginLeft: [ '0px' , '100px'] },
-                                           20 * MS_PER_SEC);
-    assert_equals(transition.playState, 'finished');
-  });
-}, 'Test for setting a new keyframe effect with a shorter duration');
+  const transition = div.getAnimations()[0];
+  await transition.ready;
 
-promise_test(function(t) {
-  var div = addDiv(t);
-  var watcher = new EventWatcher(t, div, [ 'transitionend',
-                                           'transitioncancel' ]);
+  transition.currentTime = 50 * MS_PER_SEC;
+  transition.effect = new KeyframeEffect(div,
+                                         { left: [ '0px' , '100px'] },
+                                         20 * MS_PER_SEC);
+
+  assert_equals(transition.playState, 'finished');
+}, 'After setting a new keyframe effect with a shorter duration,'
+   + ' the transition becomes finished');
+
+promise_test(async t => {
+  const div = addDiv(t);
   div.style.left = '0px';
   div.style.width = '0px';
 
   div.style.transition = 'left 100s';
-  flushComputedStyle(div);
+  getComputedStyle(div).left;
   div.style.left = '100px';
 
-  var transition = div.getAnimations()[0];
+  const transition = div.getAnimations()[0];
+  transition.effect = new KeyframeEffect(div,
+                                         { marginLeft: [ '0px' , '100px'] },
+                                         100 * MS_PER_SEC);
+  assert_equals(transition.transitionProperty, 'left');
+}, 'After setting a new keyframe effect targeting different properties,'
+   + ' the transition continues to report the original transition property');
+
+promise_test(async t => {
+  const div = addDiv(t);
+  div.style.left = '0px';
+  div.style.width = '0px';
+
+  div.style.transition = 'left 100s';
+  getComputedStyle(div).left;
+  div.style.left = '100px';
+
+  const transition = div.getAnimations()[0];
   assert_true(transition.pending);
 
   transition.effect = new KeyframeEffect(div,
                                          { marginLeft: [ '0px' , '100px'] },
                                          100 * MS_PER_SEC);
   assert_equals(transition.transitionProperty, 'left');
   assert_true(transition.pending);
 
-  return transition.ready.then(function() {
-    assert_false(transition.pending);
-  });
-}, 'Test for setting a new keyframe effect to a pending transition');
+  // As a sanity check, check that the transition actually exits the
+  // pending state.
+  await transition.ready;
+  assert_false(transition.pending);
+}, 'After setting a new keyframe effect on a play-pending transition,'
+   + ' the transition remains pending');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_animation-finished.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-finished.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-finished.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-finished.tentative.html
@@ -1,63 +1,40 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSTransition.finished</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/helper.js"></script>
 <style>
 
 .animated-div {
   margin-left: 100px;
-  transition: margin-left 1000s linear 1000s;
+  transition: margin-left 100s linear 100s;
 }
 
 </style>
-<body>
 <div id="log"></div>
 <script>
 
 'use strict';
 
-const ANIM_DELAY_MS = 1000000; // 1000s
-const ANIM_DUR_MS = 1000000; // 1000s
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, { class: 'animated-div' });
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
   animation.finish();
-
-  animation.finished.then(t.step_func(function() {
-    animation.play();
-    assert_equals(animation.currentTime, 0,
-                  'Replaying a finished transition should reset its ' +
-                  'currentTime');
-    t.done();
-  }));
-}, 'Test restarting a finished transition');
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
+  await animation.finished;
 
-  var animation = div.getAnimations()[0];
-
-  animation.ready.then(function() {
-    animation.playbackRate = -1;
-    return animation.finished;
-  }).then(t.step_func(function() {
-    animation.play();
-    // FIXME: once animation.effect.computedTiming.endTime is available (bug
-    // 1108055) we should use that here.
-    assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
-                  'Replaying a finished reversed transition should reset ' +
-                  'its currentTime to the end of the effect');
-    t.done();
-  }));
-}, 'Test restarting a reversed finished transition');
+  animation.play();
+  const marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+  assert_equals(
+    marginLeft,
+    100,
+    'Replaying a finished transition should update the target element\'s style'
+  );
+}, 'Restarting a finished transition rewinds playback');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_animation-ready.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-ready.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-ready.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-ready.tentative.html
@@ -1,98 +1,73 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSTransition.ready</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-async_test(function(t) {
-  var div = addDiv(t);
-  div.style.transform = 'translate(0px)';
-  getComputedStyle(div).transform;
-  div.style.transition = 'transform 100s';
-  div.style.transform = 'translate(10px)';
-  getComputedStyle(div).transform;
-
-  var animation = div.getAnimations()[0];
-  var originalReadyPromise = animation.ready;
-
-  animation.ready.then(t.step_func(function() {
-    assert_equals(animation.ready, originalReadyPromise,
-                  'Ready promise is the same object when playing completes');
-    animation.pause();
-    assert_not_equals(animation.ready, originalReadyPromise,
-                      'Ready promise object identity differs when pausing');
-    t.done();
-  }));
-}, 'A new ready promise is created each time play() is called'
-   + ' the animation property');
-
-async_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
   // Set up pending transition
   div.style.transform = 'translate(0px)';
   getComputedStyle(div).transform;
   div.style.transition = 'transform 100s';
   div.style.transform = 'translate(10px)';
   getComputedStyle(div).transform;
 
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_true(animation.pending, 'Animation is initially pending');
-
-  // Set up listeners on ready promise
-  animation.ready.then(t.step_func(function() {
-    assert_unreached('ready promise was fulfilled');
-  })).catch(t.step_func(function(err) {
-    assert_equals(err.name, 'AbortError',
-                  'ready promise is rejected with AbortError');
-    assert_equals(animation.playState, 'idle',
-                  'Animation is idle after transition was cancelled');
-  })).then(t.step_func(function() {
-    t.done();
-  }));
+  const readyPromise = animation.ready;
 
   // Now remove transform from transition-property and flush styles
   div.style.transitionProperty = 'none';
   getComputedStyle(div).transitionProperty;
 
-}, 'ready promise is rejected when a transition is cancelled by updating'
+  try {
+    await readyPromise;
+    assert_unreached('ready promise was fulfilled');
+  } catch (err) {
+    assert_equals(err.name, 'AbortError',
+                  'ready promise is rejected with AbortError');
+    assert_equals(animation.playState, 'idle',
+                  'Animation is idle after transition was canceled');
+  }
+}, 'ready promise is rejected when a transition is canceled by updating'
    + ' transition-property');
 
-async_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
   // Set up pending transition
   div.style.marginLeft = '0px';
   getComputedStyle(div).marginLeft;
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '100px';
   getComputedStyle(div).marginLeft;
 
-  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
-  animation.ready.then(t.step_func(function() {
+  // Update the transition to animate to something not-interpolable
+  div.style.marginLeft = 'auto';
+  getComputedStyle(div).marginLeft;
+
+  try {
+    await readyPromise;
     assert_unreached('ready promise was fulfilled');
-  })).catch(t.step_func(function(err) {
+  } catch (err) {
     assert_equals(err.name, 'AbortError',
                   'ready promise is rejected with AbortError');
     assert_equals(animation.playState, 'idle',
-                  'Animation is idle after transition was cancelled');
-  })).then(t.step_func(function() {
-    t.done();
-  }));
-
-  // Now update the transition to animate to something not-interpolable
-  div.style.marginLeft = 'auto';
-  getComputedStyle(div).marginLeft;
-
-}, 'ready promise is rejected when a transition is cancelled by changing'
+                  'Animation is idle after transition was canceled');
+  }
+}, 'ready promise is rejected when a transition is canceled by changing'
    + ' the transition property to something not interpolable');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_animation-starttime.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-startTime.tentative.html
--- a/dom/animation/test/css-transitions/test_animation-starttime.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-startTime.tentative.html
@@ -1,286 +1,116 @@
 <!doctype html>
-<html>
-  <head>
-    <meta charset=utf-8>
-    <title>Tests for the effect of setting a CSS transition's
-           Animation.startTime</title>
-    <style>
-
-.animated-div {
-  margin-left: 100px;
-  transition: margin-left 1000s linear 1000s;
-}
-
-    </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">
+<meta charset=utf-8>
+<title>CSSTransition.startTime</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.js"></script>
+<div id="log"></div>
+<script>
 
 'use strict';
 
-// 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
+test(t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
-
-const ANIM_DELAY_MS = 1000000; // 1000s
-const ANIM_DUR_MS = 1000000; // 1000s
+  assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'The start time of a newly-created transition is unresolved');
 
-/**
- * These helpers get the value that the startTime needs to be set to, to put an
- * animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
- * middle of various phases or points through the active duration.
- */
-function startTimeForBeforePhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS / 2;
-}
-function startTimeForActivePhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
-}
-function startTimeForAfterPhase(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
-}
-function startTimeForStartOfActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS;
-}
-function startTimeForFiftyPercentThroughActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
-}
-function startTimeForEndOfActiveInterval(timeline) {
-  return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
-}
+promise_test(async t => {
+  const div = addDiv(t);
 
+  div.style.left = '0px';
+  div.style.top = '0px';
+  getComputedStyle(div).transitionProperty;
 
-// Expected computed 'margin-left' values at points during the active interval:
-// When we assert_between_inclusive using these values we could in theory cause
-// intermittent failure due to very long delays between paints, but since the
-// active duration is 1000s long, a delay would need to be around 100s to cause
-// that. If that's happening then there are likely other issues that should be
-// fixed, so a failure to make us look into that seems like a good thing.
-const INITIAL_POSITION = 100;
-const TEN_PCT_POSITION = 110;
-const FIFTY_PCT_POSITION = 150;
-const END_POSITION = 200;
+  div.style.transition = 'all 100s';
+  div.style.left = '100px';
+  div.style.top = '100px';
 
-// The terms used for the naming of the following helper functions refer to
-// terms used in the Web Animations specification for specific phases of an
-// animation. The terms can be found here:
-//
-//   https://drafts.csswg.org/web-animations/#animation-effect-phases-and-states
-//
-// Note the distinction between the "animation start time" which occurs before
-// the start delay and the start of the active interval which occurs after it.
-
-// Called when the ready Promise's callbacks should happen
-function checkStateOnReadyPromiseResolved(animation)
-{
-  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');
+  let animations = div.getAnimations();
+  assert_equals(animations.length, 2);
+  await waitForAllAnimations(animations);
 
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" on the first paint ' +
-    'tick after animation creation');
+  assert_equals(animations[0].startTime, animations[1].startTime,
+    'CSS transitions started together have the same start time');
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, INITIAL_POSITION,
-                'the computed value of margin-left should be unaffected ' +
-                'by an animation with a delay on ready Promise resolve');
-}
+  await waitForFrame();
 
-// Called when startTime is set to the time the active interval starts.
-function checkStateAtActiveIntervalStartTime(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
-
-  assert_equals(animation.playState, 'running',
-    'Animation.playState should be "running" at the start of ' +
-    'the active interval');
+  div.style.backgroundColor = 'green';
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
-    'the computed value of margin-left should be close to the value at the ' +
-    'beginning of the animation');
-}
-
-function checkStateAtFiftyPctOfActiveInterval(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
+  animations = div.getAnimations();
+  assert_equals(animations.length, 3);
+  await waitForAllAnimations(animations);
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, FIFTY_PCT_POSITION,
-    'the computed value of margin-left should be half way through the ' +
-    'animation at the midpoint of the active interval');
-}
-
-// Called when startTime is set to the time the active interval ends.
-function checkStateAtActiveIntervalEndTime(animation)
-{
-  // We don't test animation.startTime since our caller just set it.
-
-  assert_equals(animation.playState, 'finished',
-    'Animation.playState should be "finished" at the end of ' +
-    'the active interval');
+  assert_less_than(animations[1].startTime, animations[2].startTime,
+    'A CSS transition added in a later frame has a later start time');
+}, 'The start time of transitions is based on when they are generated');
 
-  var div = animation.effect.target;
-  var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
-  assert_equals(marginLeft, END_POSITION,
-    'the computed value of margin-left should be the final transitioned-to ' +
-    'value at the end of the active duration');
-}
+test(t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-  assert_equals(animation.startTime, null, 'startTime is unresolved');
-}, 'startTime of a newly created transition is unresolved');
-
+  const timelineTime = animation.timeline.currentTime;
+  animation.startTime = timelineTime;
 
-test(function(t)
-{
-  var div = addDiv(t, {'class': 'animated-div'});
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-  var currentTime = animation.timeline.currentTime;
-  animation.startTime = currentTime;
-  assert_times_equal(animation.startTime, currentTime,
-    'Check setting of startTime actually works');
-}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
-   'startTime');
-
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, 'transitionend');
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-
-  animation.ready.then(t.step_func(function() {
-    checkStateOnReadyPromiseResolved(animation);
-
-    animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
-    checkStateAtActiveIntervalStartTime(animation);
+  assert_times_equal(
+    animation.startTime,
+    timelineTime,
+    'Check setting of startTime actually works'
+  );
+}, 'The start time of a transition can be set');
 
-    animation.startTime =
-      startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
-    checkStateAtFiftyPctOfActiveInterval(animation);
-
-    animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
-    return eventWatcher.wait_for('transitionend');
-  })).then(t.step_func(function() {
-    checkStateAtActiveIntervalEndTime(animation);
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(function() {
-    t.done();
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
   });
-}, 'Skipping forward through animation');
-
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
-test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-  var eventWatcher = new EventWatcher(t, div, 'transitionend');
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
+  await animation.ready;
 
-  // Unlike in the case of CSS animations, we cannot skip to the end and skip
-  // backwards since when we reach the end the transition effect is removed and
-  // changes to the Animation object no longer affect the element. For
-  // this reason we only skip forwards as far as the 90% through point.
+  const timelineTime = animation.timeline.currentTime;
+  const marginLeft = () => parseFloat(getComputedStyle(div).marginLeft);
 
-  animation.startTime =
-    startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
-  checkStateAtFiftyPctOfActiveInterval(animation);
-
-  animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
+  animation.startTime = timelineTime - 100 * MS_PER_SEC;
+  assert_equals(marginLeft(), 100);
 
-  // Despite going backwards from being in the active interval to being before
-  // it, we now expect an 'animationend' event because the animation should go
-  // from being active to inactive.
-  //
-  // Calling checkStateAtActiveIntervalStartTime will check computed style,
-  // causing computed style to be updated and the 'transitionend' event to
-  // be dispatched synchronously. We need to call waitForEvent first
-  // otherwise eventWatcher will assert that the event was unexpected.
-  eventWatcher.wait_for('transitionend').then(function() {
-    t.done();
-  });
-  checkStateAtActiveIntervalStartTime(animation);
-}, 'Skipping backwards through transition');
-
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
-
-  var storedCurrentTime;
+  animation.startTime = timelineTime - 150 * MS_PER_SEC;
+  assert_equals(marginLeft(), 150);
+}, 'The start time can be set to seek a transition');
 
-  animation.ready.then(t.step_func(function() {
-    storedCurrentTime = animation.currentTime;
-    animation.startTime = null;
-    return animation.ready;
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(t.step_func(function() {
-    assert_equals(animation.currentTime, storedCurrentTime,
-      'Test that hold time is correct');
-    t.done();
-  }));
-}, 'Setting startTime to null');
-
-
-async_test(function(t) {
-  var div = addDiv(t, {'class': 'animated-div'});
-
-  flushComputedStyle(div);
-  div.style.marginLeft = '200px'; // initiate transition
-
-  var animation = div.getAnimations()[0];
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'margin-left: 100px; transition: margin-left 100s linear 100s',
+  });
+  const eventWatcher = new EventWatcher(t, div, [
+    'transitionstart',
+    'transitionend',
+  ]);
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '200px';
+  const animation = div.getAnimations()[0];
 
-  animation.ready.then(t.step_func(function() {
-    var savedStartTime = animation.startTime;
+  await animation.ready;
 
-    assert_not_equals(animation.startTime, null,
-      'Animation.startTime not null on ready Promise resolve');
+  const timelineTime = animation.timeline.currentTime;
 
-    animation.pause();
-    return animation.ready;
-  })).then(t.step_func(function() {
-    assert_equals(animation.startTime, null,
-      'Animation.startTime is null after paused');
-    assert_equals(animation.playState, 'paused',
-      'Animation.playState is "paused" after pause() call');
-  })).catch(t.step_func(function(reason) {
-    assert_unreached(reason);
-  })).then(function() {
-    t.done();
-  });
-}, 'Animation.startTime after paused');
+  animation.startTime = timelineTime - 100 * MS_PER_SEC;
+  await eventWatcher.wait_for('transitionstart');
 
-    </script>
-  </body>
-</html>
+  animation.startTime = timelineTime - 200 * MS_PER_SEC;
+  await eventWatcher.wait_for('transitionend');
+}, 'Seeking a transition using start time dispatches transition events');
+
+</script>
rename from dom/animation/test/css-transitions/test_csstransition-transitionproperty.html
rename to testing/web-platform/tests/css/css-transitions/CSSTransition-transitionProperty.tentative.html
--- a/dom/animation/test/css-transitions/test_csstransition-transitionproperty.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-transitionProperty.tentative.html
@@ -1,26 +1,28 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSTransition.transitionProperty</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#dom-csstransition-transitionproperty">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
-  // Add a transition
   div.style.left = '0px';
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'all 100s';
   div.style.left = '100px';
 
-  assert_equals(div.getAnimations()[0].transitionProperty, 'left',
-                'The transitionProperty for the corresponds to the specific ' +
-                'property being transitioned');
+  assert_equals(
+    div.getAnimations()[0].transitionProperty,
+    'left',
+    'The transitionProperty for the returned transition corresponds to the'
+    + ' specific property being transitioned'
+  );
 }, 'CSSTransition.transitionProperty');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_document-get-animations.html
rename to testing/web-platform/tests/css/css-transitions/Document-getAnimations.tentative.html
--- a/dom/animation/test/css-transitions/test_document-get-animations.html
+++ b/testing/web-platform/tests/css/css-transitions/Document-getAnimations.tentative.html
@@ -1,26 +1,27 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Document.getAnimations() for CSS transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#animation-composite-order">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <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 a couple of transitions
   div.style.left = '0px';
   div.style.top = '0px';
   getComputedStyle(div).transitionProperty;
 
   div.style.transition = 'all 100s';
   div.style.left = '100px';
@@ -29,67 +30,65 @@ test(function(t) {
                 'getAnimations returns two running CSS Transitions');
 
   // Remove both
   div.style.transitionProperty = 'none';
   assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Transitions');
 }, 'getAnimations for CSS Transitions');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
                                 'transition: all 100s;',
                 '.init::before': 'content: ""; width: 0px; ' +
                                  'transition: all 10s;',
                 '.change::after': 'width: 100px;',
                 '.change::before': 'width: 100px;' });
   // create two divs with these arrangement:
   //       parent
   //     ::before,
   //     ::after
   //        |
   //       child
-  var parent = addDiv(t);
-  var child = addDiv(t);
+  const parent = addDiv(t);
+  const child = addDiv(t);
   parent.appendChild(child);
 
   parent.style.left = '0px';
   parent.style.transition = 'left 10s';
   parent.classList.add('init');
   child.style.left = '0px';
   child.style.transition = 'left 10s';
-  flushComputedStyle(parent);
+  getComputedStyle(parent).left;
 
   parent.style.left = '100px';
   parent.classList.add('change');
   child.style.left = '100px';
 
-  var anims = document.getAnimations();
+  const anims = document.getAnimations();
   assert_equals(anims.length, 4,
                 'CSS transition 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',
                 'The animation targeting the ::after element comes third');
   assert_equals(anims[3].effect.target, child,
                 'The animation targeting the child element comes last');
 }, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
    'after sorting');
 
-async_test(function(t) {
-  var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
-  flushComputedStyle(div);
+promise_test(async t => {
+  const div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
+  getComputedStyle(div).left;
 
   div.style.left = '100px';
-  var animations = div.getAnimations();
+  const animations = div.getAnimations();
   assert_equals(animations.length, 1, 'Got transition');
-  animations[0].finished.then(t.step_func(function() {
-    assert_equals(document.getAnimations().length, 0,
-                  'No animations returned');
-    t.done();
-  }));
+  await animations[0].finished;
+
+  assert_equals(document.getAnimations().length, 0,
+                'No animations returned');
 }, 'Transitions are not returned after they have finished');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_element-get-animations.html
rename to testing/web-platform/tests/css/css-transitions/Element-getAnimations.tentative.html
--- a/dom/animation/test/css-transitions/test_element-get-animations.html
+++ b/testing/web-platform/tests/css/css-transitions/Element-getAnimations.tentative.html
@@ -1,149 +1,117 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>Element.getAnimations() for CSS transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#animation-composite-order">
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-async_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
-  // FIXME: This test does too many things. It should be split up.
-
-  // Add a couple of transitions
   div.style.left = '0px';
   div.style.top = '0px';
   getComputedStyle(div).transitionProperty;
 
   div.style.transition = 'all 100s';
   div.style.left = '100px';
   div.style.top = '100px';
 
-  var animations = div.getAnimations();
-  assert_equals(animations.length, 2,
-    'getAnimations() returns one Animation per transitioning property');
-  waitForAllAnimations(animations).then(t.step_func(function() {
-    var startTime = animations[0].startTime;
-    assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
-                'CSS transitions have sensible start times');
-    assert_equals(animations[0].startTime, animations[1].startTime,
-      'CSS transitions started together have the same start time');
-    // Wait a moment then add a third transition
-    return waitForFrame();
-  })).then(t.step_func(function() {
-    div.style.backgroundColor = 'green';
-    animations = div.getAnimations();
-    assert_equals(animations.length, 3,
-      'getAnimations returns Animations for all running CSS Transitions');
-    return waitForAllAnimations(animations);
-  })).then(t.step_func(function() {
-    assert_less_than(animations[1].startTime, animations[2].startTime,
-      'Animation for additional CSS transition starts after the original'
-      + ' transitions and appears later in the list');
-    t.done();
-  }));
-}, 'getAnimations for CSS Transitions');
+  assert_equals(div.getAnimations().length, 2);
+}, 'getAnimations returns one Animation per transitioning property');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'left: 0px; transition: all 100s' });
+test(t => {
+  const div = addDiv(t, { style: 'left: 0px; transition: all 100s' });
 
-  flushComputedStyle(div);
+  getComputedStyle(div).left;
   div.style.left = '100px';
 
   assert_class_string(div.getAnimations()[0], 'CSSTransition',
                       'Interface of returned animation is CSSTransition');
 }, 'getAnimations returns CSSTransition objects for CSS Transitions');
 
-async_test(function(t) {
-  var div = addDiv(t);
+promise_test(async t => {
+  const div = addDiv(t);
 
-  // Set up event listener
-  div.addEventListener('transitionend', t.step_func(function() {
-    assert_equals(div.getAnimations().length, 0,
-      'getAnimations does not return finished CSS Transitions');
-    t.done();
-  }));
-
-  // Add a very short transition
   div.style.left = '0px';
   getComputedStyle(div).left;
-
   div.style.transition = 'all 0.01s';
   div.style.left = '100px';
-  getComputedStyle(div).left;
-}, 'getAnimations for CSS Transitions that have finished');
+  const animation = div.getAnimations()[0];
+
+  await animation.finished;
 
-test(function(t) {
-  var div = addDiv(t);
+  assert_equals(div.getAnimations().length, 0);
+}, 'getAnimations does not return finished CSS Transitions');
 
-  // Try to transition non-animatable property animation-duration
+test(t => {
+  const div = addDiv(t);
+
+  // animation-duration is not animatable
   div.style.animationDuration = '10s';
   getComputedStyle(div).animationDuration;
 
   div.style.transition = 'all 100s';
   div.style.animationDuration = '100s';
 
-  assert_equals(div.getAnimations().length, 0,
-    'getAnimations returns an empty sequence for a transition'
-    + ' of a non-animatable property');
-}, 'getAnimations for transition on non-animatable property');
+  assert_equals(div.getAnimations().length, 0);
+}, 'getAnimations does not return a transition for a non-animatable property');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.setProperty('-vendor-unsupported', '0px', '');
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'all 100s';
   div.style.setProperty('-vendor-unsupported', '100px', '');
 
-  assert_equals(div.getAnimations().length, 0,
-    'getAnimations returns an empty sequence for a transition'
-    + ' of an unsupported property');
-}, 'getAnimations for transition on unsupported property');
+  assert_equals(div.getAnimations().length, 0);
+}, 'getAnimations does not return a transition for an unsupposed property');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'transform: translate(0px); ' +
-                               'opacity: 0; ' +
-                               'border-width: 0px; ' + // Shorthand
-                               'border-style: solid' });
+test(t => {
+  const div = addDiv(t, { style: 'transform: translate(0px); ' +
+                                 'opacity: 0; ' +
+                                 'border-width: 0px; ' + // Shorthand
+                                 'border-style: solid' });
   getComputedStyle(div).transform;
 
   div.style.transition = 'all 100s';
   div.style.transform = 'translate(100px)';
   div.style.opacity = '1';
   div.style.borderWidth = '1px';
 
-  var animations = div.getAnimations();
+  const animations = div.getAnimations();
   assert_equals(animations.length, 6,
                 'Generated expected number of transitions');
   assert_equals(animations[0].transitionProperty, 'border-bottom-width');
   assert_equals(animations[1].transitionProperty, 'border-left-width');
   assert_equals(animations[2].transitionProperty, 'border-right-width');
   assert_equals(animations[3].transitionProperty, 'border-top-width');
   assert_equals(animations[4].transitionProperty, 'opacity');
   assert_equals(animations[5].transitionProperty, 'transform');
 }, 'getAnimations sorts simultaneous transitions by name');
 
-test(function(t) {
-  var div = addDiv(t, { style: 'transform: translate(0px); ' +
-                               'opacity: 0' });
+test(t => {
+  const div = addDiv(t, { style: 'transform: translate(0px); ' +
+                                 'opacity: 0' });
   getComputedStyle(div).transform;
 
   div.style.transition = 'all 100s';
   div.style.transform = 'translate(100px)';
   assert_equals(div.getAnimations().length, 1,
                 'Initially there is only one (transform) transition');
   div.style.opacity = '1';
   assert_equals(div.getAnimations().length, 2,
                 'Then a second (opacity) transition is added');
 
-  var animations = div.getAnimations();
+  const animations = div.getAnimations();
   assert_equals(animations[0].transitionProperty, 'transform');
   assert_equals(animations[1].transitionProperty, 'opacity');
 }, 'getAnimations sorts transitions by when they were generated');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html
rename to testing/web-platform/tests/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html
--- a/dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html
+++ b/testing/web-platform/tests/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html
@@ -1,103 +1,135 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>KeyframeEffect.getKeyframes() for CSS transitions</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
+<script src="support/helper.js"></script>
 <style>
 :root {
   --var-100px: 100px;
 }
 </style>
-<body>
 <div id="log"></div>
 <script>
 'use strict';
 
-function getKeyframes(e) {
+const getKeyframes = e => {
   return e.getAnimations()[0].effect.getKeyframes();
-}
+};
 
-function 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) {
-    assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
+const assert_frames_equal = (a, b, name) => {
+  assert_equals(
+    Object.keys(a).sort().toString(),
+    Object.keys(b).sort().toString(),
+    `properties on ${name}`
+  );
+  for (const p in a) {
+    assert_equals(a[p], b[p], `value for '${p}' on ${name}`);
   }
-}
+};
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.left = '0px';
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'left 100s';
   div.style.left = '100px';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
-  assert_equals(frames.length, 2, "number of frames");
+  assert_equals(frames.length, 2, 'number of frames');
 
-  var expected = [
-    { offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
-      left: "0px" },
-    { offset: 1, computedOffset: 1, easing: "linear", composite: "auto",
-      left: "100px" },
+  const expected = [
+    { offset: 0,
+      computedOffset: 0,
+      easing: 'ease',
+      composite: 'auto',
+      left: '0px',
+    },
+    {
+      offset: 1,
+      computedOffset: 1,
+      easing: 'linear',
+      composite: 'auto',
+      left: '100px',
+    },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
-    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + 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'
    + ' transition');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.left = '0px';
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'left 100s steps(2,end)';
   div.style.left = '100px';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
-  assert_equals(frames.length, 2, "number of frames");
+  assert_equals(frames.length, 2, 'number of frames');
 
-  var expected = [
-    { offset: 0, computedOffset: 0, easing: "steps(2)", composite: "auto",
-      left: "0px" },
-    { offset: 1, computedOffset: 1, easing: "linear", composite: "auto",
-      left: "100px" },
+  const expected = [
+    {
+      offset: 0,
+      computedOffset: 0,
+      easing: 'steps(2)',
+      composite: 'auto',
+      left: '0px',
+    },
+    {
+      offset: 1,
+      computedOffset: 1,
+      easing: 'linear',
+      composite: 'auto',
+      left: '100px',
+    },
   ];
 
-  for (var i = 0; i < frames.length; i++) {
-    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + 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'
    + ' transition with a non-default easing function');
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
   div.style.left = '0px';
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'left 100s';
   div.style.left = 'var(--var-100px)';
 
-  var frames = getKeyframes(div);
+  const frames = getKeyframes(div);
 
   // CSS transition endpoints are based on the computed value so we
   // shouldn't see the variable reference
-  var expected = [
-    { offset: 0, computedOffset: 0, easing: 'ease', composite: 'auto',
-      left: '0px' },
-    { offset: 1, computedOffset: 1, easing: 'linear', composite: 'auto',
-      left: '100px' },
+  const expected = [
+    {
+      offset: 0,
+      computedOffset: 0,
+      easing: 'ease',
+      composite: 'auto',
+      left: '0px',
+    },
+    {
+      offset: 1,
+      computedOffset: 1,
+      easing: 'linear',
+      composite: 'auto',
+      left: '100px',
+    },
   ];
-  for (var i = 0; i < frames.length; i++) {
-    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + 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'
    + ' transition with a CSS variable endpoint');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_effect-target.html
rename to testing/web-platform/tests/css/css-transitions/KeyframeEffect-target.tentative.html
--- a/dom/animation/test/css-transitions/test_effect-target.html
+++ b/testing/web-platform/tests/css/css-transitions/KeyframeEffect-target.tentative.html
@@ -1,68 +1,69 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>CSSTransition.effect.target</title>
+<!-- TODO: Add a more specific link for this once it is specified. -->
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#csstransition">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 
-test(function(t) {
-  var div = addDiv(t);
+test(t => {
+  const div = addDiv(t);
 
   div.style.left = '0px';
   getComputedStyle(div).transitionProperty;
   div.style.transition = 'left 100s';
   div.style.left = '100px';
 
-  var animation = div.getAnimations()[0];
+  const animation = div.getAnimations()[0];
   assert_equals(animation.effect.target, div,
     'Animation.target is the animatable div');
 }, 'Returned CSS transitions have the correct Animation.target');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
                                 'transition: all 10s;',
                 '.change::after': 'width: 100px; height: 100px;' });
-  var div = addDiv(t, { class: 'init' });
-  flushComputedStyle(div);
+  const div = addDiv(t, { class: 'init' });
+  getComputedStyle(div).width;
   div.classList.add('change');
 
-  var anims = document.getAnimations();
+  const anims = document.getAnimations();
   assert_equals(anims.length, 2,
                 'Got transitions running on ::after pseudo element');
   assert_equals(anims[0].effect.target, anims[1].effect.target,
                 'Both transitions return the same target object');
 }, 'effect.target should return the same CSSPseudoElement object each time');
 
-test(function(t) {
+test(t => {
   addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
                 '.change::after': 'width: 100px;' });
-  var div = addDiv(t, { class: 'init' });
-  flushComputedStyle(div);
+  const div = addDiv(t, { class: 'init' });
+  getComputedStyle(div).width;
   div.classList.add('change');
-  var pseudoTarget = document.getAnimations()[0].effect.target;
-  var effect = new KeyframeEffect(pseudoTarget,
-                                  { background: ["blue", "red"] },
-                                  3000);
-  var newAnim = new Animation(effect, document.timeline);
+  const pseudoTarget = document.getAnimations()[0].effect.target;
+  const effect = new KeyframeEffect(pseudoTarget,
+                                    { background: ["blue", "red"] },
+                                    3000);
+  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');
   assert_equals(anims[0].effect.target, newAnim.effect.target,
                 'Both the transition and the scripted-generated animation ' +
                 'return the same target object');
 }, 'effect.target from the script-generated animation should return the same ' +
    'CSSPseudoElement object as that from the CSS generated transition');
 
 </script>
-</body>
rename from dom/animation/test/css-transitions/test_event-dispatch.html
rename to testing/web-platform/tests/css/css-transitions/event-dispatch.tentative.html
--- a/dom/animation/test/css-transitions/test_event-dispatch.html
+++ b/testing/web-platform/tests/css/css-transitions/event-dispatch.tentative.html
@@ -1,476 +1,409 @@
 <!doctype html>
 <meta charset=utf-8>
-<title>Tests for CSS-Transition events</title>
-<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#transition-events">
+<title>CSS transition event dispatch</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#event-dispatch">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="../testcommon.js"></script>
-<body>
+<script src="support/helper.js"></script>
 <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 TransitionEventHandler(target) {
-  this.target = target;
-  this.target.ontransitionrun = evt => {
-    this.transitionrun = evt.elapsedTime;
-  };
-  this.target.ontransitionstart = evt => {
-    this.transitionstart = evt.elapsedTime;
-  };
-  this.target.ontransitionend = evt => {
-    this.transitionend = evt.elapsedTime;
-  };
-  this.target.ontransitioncancel = evt => {
-    this.transitioncancel = evt.elapsedTime;
-  };
-}
-
-TransitionEventHandler.prototype.clear = () => {
-  this.transitionrun    = undefined;
-  this.transitionstart  = undefined;
-  this.transitionend    = undefined;
-  this.transitioncancel = undefined;
-};
-
-function setupTransition(t, transitionStyle) {
+const setupTransition = (t, transitionStyle) => {
   const div = addDiv(t, { style: 'transition: ' + transitionStyle });
-  // Note that this TransitionEventHandler 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 TransitionEventHandler(div);
   const watcher = new EventWatcher(t, div, [ 'transitionrun',
-                                           'transitionstart',
-                                           'transitionend',
-                                           'transitioncancel' ]);
-  flushComputedStyle(div);
+                                             'transitionstart',
+                                             'transitionend',
+                                             'transitioncancel' ]);
+  getComputedStyle(div).marginLeft;
 
   div.style.marginLeft = '100px';
   const transition = div.getAnimations()[0];
 
-  return { transition, watcher, div, handler };
-}
+  return { transition, watcher, div };
+};
 
 // On the next frame (i.e. when events are queued), whether or not the
 // transition is still pending depends on the implementation.
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher } =
     setupTransition(t, 'margin-left 100s 100s');
-  return watcher.wait_for('transitionrun').then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  const evt = await watcher.wait_for('transitionrun');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Idle -> Pending or Before');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher } =
     setupTransition(t, 'margin-left 100s 100s');
   // Force the transition to leave the idle phase
   transition.startTime = document.timeline.currentTime;
-  return watcher.wait_for('transitionrun').then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  const evt = await watcher.wait_for('transitionrun');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Idle -> Before');
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
 
   // Seek to Active phase.
   transition.currentTime = 100 * MS_PER_SEC;
   transition.pause();
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    assert_equals(handler.transitionrun, 0.0);
-    assert_equals(handler.transitionstart, 0.0);
+  const events = await watcher.wait_for(['transitionrun', 'transitionstart'], {
+    record: 'all',
   });
+  assert_equals(events[0].elapsedTime, 0.0);
+  assert_equals(events[1].elapsedTime, 0.0);
 }, 'Idle or Pending -> Active');
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
 
   // Seek to After phase.
   transition.finish();
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart',
-                            'transitionend' ]).then(evt => {
-    assert_equals(handler.transitionrun, 0.0);
-    assert_equals(handler.transitionstart, 0.0);
-    assert_equals(handler.transitionend, 100.0);
-  });
+  const events = await watcher.wait_for(
+    ['transitionrun', 'transitionstart', 'transitionend'],
+    {
+      record: 'all',
+    }
+  );
+  assert_equals(events[0].elapsedTime, 0.0);
+  assert_equals(events[1].elapsedTime, 0.0);
+  assert_equals(events[2].elapsedTime, 100.0);
 }, 'Idle or Pending -> After');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return Promise.all([ watcher.wait_for('transitionrun'),
-                       transition.ready ]).then(() => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Before -> Idle (display: none)');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s 100s');
-
-  return Promise.all([ watcher.wait_for('transitionrun'),
-                       transition.ready ]).then(() => {
-    // Make idle
-    transition.timeline = null;
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
-}, 'Before -> Idle (Animation.timeline = null)');
-
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return Promise.all([ watcher.wait_for('transitionrun'),
-                       transition.ready ]).then(() => {
-    transition.currentTime = 100 * MS_PER_SEC;
-    return watcher.wait_for('transitionstart');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
-}, 'Before -> Active');
+  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
+  // Make idle
+  transition.timeline = null;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
+}, 'Before -> Idle (Animation.timeline = null)');
+
+promise_test(async t => {
+  const { transition, watcher } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return Promise.all([ watcher.wait_for('transitionrun'),
-                       transition.ready ]).then(() => {
-    // Seek to After phase.
-    transition.currentTime = 200 * MS_PER_SEC;
-    return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
-  }).then(evt => {
-    assert_equals(handler.transitionstart, 0.0);
-    assert_equals(handler.transitionend, 100.0);
+  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);
+
+  transition.currentTime = 100 * MS_PER_SEC;
+  const evt = await watcher.wait_for('transitionstart');
+  assert_equals(evt.elapsedTime, 0.0);
+}, 'Before -> Active');
+
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
+
+  await Promise.all([ watcher.wait_for('transitionrun'), transition.ready ]);
+  // Seek to After phase.
+  transition.currentTime = 200 * MS_PER_SEC;
+  const events = await watcher.wait_for(['transitionstart', 'transitionend'], {
+    record: 'all',
   });
+
+  assert_equals(events[0].elapsedTime, 0.0);
+  assert_equals(events[1].elapsedTime, 100.0);
 }, 'Before -> After');
 
-promise_test(t => {
-  const { transition, watcher, div } =
-    setupTransition(t, 'margin-left 100s');
+promise_test(async t => {
+  const { transition, watcher, div } = setupTransition(t, 'margin-left 100s');
 
   // Seek to Active start position.
   transition.pause();
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, no delay (display: none)');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s');
+
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    transition.currentTime = 0;
-    transition.timeline = null;
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  // Make idle
+  transition.currentTime = 0;
+  transition.timeline = null;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, no delay (Animation.timeline = null)');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s 100s');
   // Pause so the currentTime is fixed and we can accurately compare the event
   // time in transition cancel events.
   transition.pause();
 
   // Seek to Active phase.
   transition.currentTime = 100 * MS_PER_SEC;
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, with positive delay (display: none)');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
 
   // Seek to Active phase.
   transition.currentTime = 100 * MS_PER_SEC;
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    transition.currentTime = 100 * MS_PER_SEC;
-    transition.timeline = null;
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Make idle
+  transition.currentTime = 100 * MS_PER_SEC;
+  transition.timeline = null;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, with positive delay (Animation.timeline = null)');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s -50s');
 
   // Pause so the currentTime is fixed and we can accurately compare the event
   // time in transition cancel events.
   transition.pause();
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 50.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 50.0);
 }, 'Active -> Idle, with negative delay (display: none)');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s -50s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s -50s');
+
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    transition.currentTime = 50 * MS_PER_SEC;
-    transition.timeline = null;
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  // Make idle
+  transition.currentTime = 50 * MS_PER_SEC;
+  transition.timeline = null;
+  const evt = await watcher.wait_for('transitioncancel');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Idle, with negative delay (Animation.timeline = null)');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
+
   // Seek to Active phase.
   transition.currentTime = 100 * MS_PER_SEC;
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Seek to Before phase.
-    transition.currentTime = 0;
-    return watcher.wait_for('transitionend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 0.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Seek to Before phase.
+  transition.currentTime = 0;
+  const evt = await watcher.wait_for('transitionend');
+  assert_equals(evt.elapsedTime, 0.0);
 }, 'Active -> Before');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
+
   // Seek to Active phase.
   transition.currentTime = 100 * MS_PER_SEC;
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Seek to After phase.
-    transition.currentTime = 200 * MS_PER_SEC;
-    return watcher.wait_for('transitionend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100.0);
-  });
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  // Seek to After phase.
+  transition.currentTime = 200 * MS_PER_SEC;
+  const evt = await watcher.wait_for('transitionend');
+  assert_equals(evt.elapsedTime, 100.0);
 }, 'Active -> After');
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
+
+  // Seek to After phase.
+  transition.finish();
+  await watcher.wait_for([ 'transitionrun',
+                           'transitionstart',
+                           'transitionend' ]);
+
+  // Seek to Before phase.
+  transition.currentTime = 0;
+  const events = await watcher.wait_for(['transitionstart', 'transitionend'], {
+    record: 'all',
+  });
+
+  assert_equals(events[0].elapsedTime, 100.0);
+  assert_equals(events[1].elapsedTime, 0.0);
+}, 'After -> Before');
+
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s 100s');
 
   // Seek to After phase.
   transition.finish();
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart',
-                            'transitionend' ]).then(evt => {
-    // Seek to Before phase.
-    transition.currentTime = 0;
-    return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
-  }).then(evt => {
-    assert_equals(handler.transitionstart, 100.0);
-    assert_equals(handler.transitionend, 0.0);
-  });
-}, 'After -> Before');
+  await watcher.wait_for([ 'transitionrun',
+                           'transitionstart',
+                           'transitionend' ]);
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s 100s');
-  // Seek to After phase.
-  transition.finish();
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart',
-                            'transitionend' ]).then(evt => {
-    // Seek to Active phase.
-    transition.currentTime = 100 * MS_PER_SEC;
-    return watcher.wait_for('transitionstart');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100.0);
-  });
+  // Seek to Active phase.
+  transition.currentTime = 100 * MS_PER_SEC;
+  const evt = await watcher.wait_for('transitionstart');
+  assert_equals(evt.elapsedTime, 100.0);
 }, 'After -> Active');
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
-    setupTransition(t, 'margin-left 100s -50s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s -50s');
+
+  const events = await watcher.wait_for(['transitionrun', 'transitionstart'], {
+    record: 'all',
+  });
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(() => {
-    assert_equals(handler.transitionrun, 50.0);
-    assert_equals(handler.transitionstart, 50.0);
-    transition.finish();
-    return watcher.wait_for('transitionend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 100.0);
-  });
+  assert_equals(events[0].elapsedTime, 50.0);
+  assert_equals(events[1].elapsedTime, 50.0);
+  transition.finish();
+
+  const evt = await watcher.wait_for('transitionend');
+  assert_equals(evt.elapsedTime, 100.0);
 }, 'Calculating the interval start and end time with negative start delay.');
 
-promise_test(t => {
-  const { transition, watcher, div, handler } =
-    setupTransition(t, 'margin-left 100s 100s');
+promise_test(async t => {
+  const { transition, watcher, div } = setupTransition(
+    t,
+    'margin-left 100s 100s'
+  );
+
+  await watcher.wait_for('transitionrun');
 
-  return watcher.wait_for('transitionrun').then(evt => {
-    // We can't set the end delay via generated effect timing
-    // because mutating CSS transitions is not specced yet.
-    transition.effect = new KeyframeEffect(div,
-                                           { marginleft: [ '0px', '100px' ]},
-                                           { duration: 100 * MS_PER_SEC,
-                                             endDelay: -50 * MS_PER_SEC });
-    // Seek to Before and play.
-    transition.cancel();
-    transition.play();
-    return watcher.wait_for([ 'transitioncancel',
-                              'transitionrun',
-                              'transitionstart' ]);
-  }).then(() => {
-    assert_equals(handler.transitionstart, 0.0);
+  // We can't set the end delay via generated effect timing
+  // because mutating CSS transitions is not specced yet.
+  transition.effect = new KeyframeEffect(
+    div,
+    { marginleft: ['0px', '100px'] },
+    {
+      duration: 100 * MS_PER_SEC,
+      endDelay: -50 * MS_PER_SEC,
+    }
+  );
+  // Seek to Before and play.
+  transition.cancel();
+  transition.play();
+  const events = await watcher.wait_for(
+    ['transitioncancel', 'transitionrun', 'transitionstart'],
+    { record: 'all' }
+  );
+  assert_equals(events[2].elapsedTime, 0.0);
 
-    // Seek to After phase.
-    transition.finish();
-    return watcher.wait_for('transitionend');
-  }).then(evt => {
-    assert_equals(evt.elapsedTime, 50.0);
-  });
+  // Seek to After phase.
+  transition.finish();
+  const evt = await watcher.wait_for('transitionend');
+  assert_equals(evt.elapsedTime, 50.0);
 }, 'Calculating the interval start and end time with negative end delay.');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return watcher.wait_for('transitionrun').then(() => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    return watcher.wait_for('transitioncancel');
-  }).then(() => {
-    transition.cancel();
-    // Then wait a couple of frames and check that no event was dispatched
-    return waitForAnimationFrames(2);
-  });
-}, 'Call Animation.cancel after cancelling transition.');
+  await watcher.wait_for('transitionrun');
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  await watcher.wait_for('transitioncancel');
 
-promise_test(t => {
+  transition.cancel();
+  // Then wait a couple of frames and check that no event was dispatched
+  await waitForAnimationFrames(2);
+}, 'Call Animation.cancel after canceling transition.');
+
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return watcher.wait_for('transitionrun').then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    transition.play();
-    watcher.wait_for([ 'transitioncancel',
-                       'transitionrun',
-                       'transitionstart' ]);
-  });
-}, 'Restart transition after cancelling transition immediately');
+  await watcher.wait_for('transitionrun');
 
-promise_test(t => {
+  // Make idle
+  transition.cancel();
+  transition.play();
+  await watcher.wait_for([ 'transitioncancel',
+                           'transitionrun' ]);
+}, 'Restart transition after canceling transition immediately');
+
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s 100s');
 
-  return watcher.wait_for('transitionrun').then(evt => {
-    // Make idle
-    div.style.display = 'none';
-    flushComputedStyle(div);
-    transition.play();
-    transition.cancel();
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    // Then wait a couple of frames and check that no event was dispatched
-    return waitForAnimationFrames(2);
-  });
+  await watcher.wait_for('transitionrun');
+
+  // Make idle
+  div.style.display = 'none';
+  getComputedStyle(div).marginLeft;
+  transition.play();
+  transition.cancel();
+  await watcher.wait_for('transitioncancel');
+
+  // Then wait a couple of frames and check that no event was dispatched
+  await waitForAnimationFrames(2);
 }, 'Call Animation.cancel after restarting transition immediately');
 
-promise_test(t => {
-  const { transition, watcher } =
-    setupTransition(t, 'margin-left 100s');
+promise_test(async t => {
+  const { transition, watcher } = setupTransition(t, 'margin-left 100s');
+
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    // Make idle
-    transition.timeline = null;
-    return watcher.wait_for('transitioncancel');
-  }).then(evt => {
-    transition.timeline = document.timeline;
-    transition.play();
+  // Make idle
+  transition.timeline = null;
+  await watcher.wait_for('transitioncancel');
 
-    return watcher.wait_for(['transitionrun', 'transitionstart']);
-  });
+  transition.timeline = document.timeline;
+  transition.play();
+
+  await watcher.wait_for(['transitionrun', 'transitionstart']);
 }, 'Set timeline and play transition after clear the timeline');
 
-promise_test(t => {
+promise_test(async t => {
   const { transition, watcher, div } =
     setupTransition(t, 'margin-left 100s');
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(() => {
-    transition.cancel();
-    return watcher.wait_for('transitioncancel');
-  }).then(() => {
-    // Make After phase
-    transition.effect = null;
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
+
+  transition.cancel();
+  await watcher.wait_for('transitioncancel');
 
-    // Then wait a couple of frames and check that no event was dispatched
-    return waitForAnimationFrames(2);
-  });
-}, 'Set null target effect after cancel the transition');
+  // Make After phase
+  transition.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 transition');
 
-promise_test(t => {
-  const { transition, watcher, div } =
-    setupTransition(t, 'margin-left 100s');
+promise_test(async t => {
+  const { transition, watcher, div } = setupTransition(t, 'margin-left 100s');
+
+  await watcher.wait_for([ 'transitionrun', 'transitionstart' ]);
 
-  return watcher.wait_for([ 'transitionrun',
-                            'transitionstart' ]).then(evt => {
-    transition.effect = null;
-    return watcher.wait_for('transitionend');
-  }).then(evt => {
-    transition.cancel();
+  transition.effect = null;
+  await watcher.wait_for('transitionend');
 
-    // Then wait a couple of frames and check that no event was dispatched
-    return waitForAnimationFrames(2);
-  });
+  transition.cancel();
+
+  // Then wait a couple of frames and check that no event was dispatched
+  await waitForAnimationFrames(2);
 }, 'Cancel the transition after clearing the target effect');
 
 </script>
-</body>
-</html>
--- a/testing/web-platform/tests/css/css-transitions/support/helper.js
+++ b/testing/web-platform/tests/css/css-transitions/support/helper.js
@@ -89,42 +89,51 @@ root.domFixture = function(selector) {
         // restore the copy
         var tmp = _domFixture.cloneNode(true);
         fixture.parentNode.replaceChild(tmp, fixture);
     } else {
         throw new Error('domFixture must be initialized first!');
     }
 };
 
+root.MS_PER_SEC = 1000;
+
 /*
  * The recommended minimum precision to use for time values.
  *
  * Based on Web Animations:
  * https://w3c.github.io/web-animations/#precision-of-time-values
  */
 const TIME_PRECISION = 0.0005; // ms
 
 /*
  * Allow implementations to substitute an alternative method for comparing
  * times based on their precision requirements.
  */
 root.assert_times_equal = function(actual, expected, description) {
   assert_approx_equals(actual, expected, TIME_PRECISION, description);
-}
+};
+
+/*
+ * Compare a time value based on its precision requirements with a fixed value.
+ */
+root.assert_time_equals_literal = (actual, expected, description) => {
+  assert_approx_equals(actual, expected, TIME_PRECISION, description);
+};
 
 /**
  * Assert that CSSTransition event, |evt|, has the expected property values
  * defined by |propertyName|, |elapsedTime|, and |pseudoElement|.
  */
 root.assert_end_events_equal = function(evt, propertyName, elapsedTime,
                                         pseudoElement = '') {
   assert_equals(evt.propertyName, propertyName);
   assert_times_equal(evt.elapsedTime, elapsedTime);
   assert_equals(evt.pseudoElement, pseudoElement);
-}
+};
 
 /**
  * Assert that array of simultaneous CSSTransition events, |evts|, have the
  * corresponding property names listed in |propertyNames|, and the expected
  * |elapsedTimes| and |pseudoElement| members.
  *
  * |elapsedTimes| may be a single value if all events are expected to have the
  * same elapsedTime, or an array parallel to |propertyNames|.
@@ -189,11 +198,81 @@ root.addDiv = function(t, attrs) {
   if (t && typeof t.add_cleanup === 'function') {
     t.add_cleanup(function() {
       if (div.parentNode) {
         div.remove();
       }
     });
   }
   return div;
-}
+};
+
+/**
+ * Appends a style div to the document head.
+ *
+ * @param t  The testharness.js Test object. If provided, this will be used
+ *           to register a cleanup callback to remove the style element
+ *           when the test finishes.
+ *
+ * @param rules  A dictionary object with selector names and rules to set on
+ *               the style sheet.
+ */
+root.addStyle = (t, rules) => {
+  const extraStyle = document.createElement('style');
+  document.head.appendChild(extraStyle);
+  if (rules) {
+    const sheet = extraStyle.sheet;
+    for (const selector in rules) {
+      sheet.insertRule(selector + '{' + rules[selector] + '}',
+                       sheet.cssRules.length);
+    }
+  }
+
+  if (t && typeof t.add_cleanup === 'function') {
+    t.add_cleanup(() => {
+      extraStyle.remove();
+    });
+  }
+};
+
+/**
+ * Promise wrapper for requestAnimationFrame.
+ */
+root.waitForFrame = () => {
+  return new Promise(resolve => {
+    window.requestAnimationFrame(resolve);
+  });
+};
+
+/**
+ * Returns a Promise that is resolved after the given number of consecutive
+ * animation frames have occured (using requestAnimationFrame callbacks).
+ *
+ * @param frameCount  The number of animation frames.
+ * @param onFrame  An optional function to be processed in each animation frame.
+ */
+root.waitForAnimationFrames = (frameCount, onFrame) => {
+  const timeAtStart = document.timeline.currentTime;
+  return new Promise(resolve => {
+    function handleFrame() {
+      if (onFrame && typeof onFrame === 'function') {
+        onFrame();
+      }
+      if (timeAtStart != document.timeline.currentTime &&
+          --frameCount <= 0) {
+        resolve();
+      } else {
+        window.requestAnimationFrame(handleFrame); // wait another frame
+      }
+    }
+    window.requestAnimationFrame(handleFrame);
+  });
+};
+
+/**
+ * Wrapper that takes a sequence of N animations and returns:
+ *
+ *   Promise.all([animations[0].ready, animations[1].ready, ... animations[N-1].ready]);
+ */
+root.waitForAllAnimations = animations =>
+  Promise.all(animations.map(animation => animation.ready));
 
 })(window);
deleted file mode 100644
index 358055b50b2003968d3ee1018db86b0399dafc82..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 295003780aa98c4b114dc60507dc468e9559ba63..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index cdc66e11245a95108d723932403401bceebf078c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -30,15 +30,12 @@ toolkit.jar:
    skin/classic/global/toolbarbutton.css
    skin/classic/global/tooltip.css
 *  skin/classic/global/tree.css
 *  skin/classic/global/alerts/alert.css                        (alerts/alert.css)
 
    skin/classic/global/icons/Authentication.png                (icons/Authentication.png)
    skin/classic/global/icons/blacklist_favicon.png             (icons/blacklist_favicon.png)
    skin/classic/global/icons/blacklist_large.png               (icons/blacklist_large.png)
-   skin/classic/global/icons/Close.gif                         (icons/Close.gif)
-   skin/classic/global/icons/Minimize.gif                      (icons/Minimize.gif)
-   skin/classic/global/icons/Restore.gif                       (icons/Restore.gif)
    skin/classic/global/icons/sslWarning.png                    (icons/sslWarning.png)
 
 *  skin/classic/global/in-content/common.css                   (in-content/common.css)
 *  skin/classic/global/in-content/info-pages.css               (in-content/info-pages.css)