Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Sat, 24 Aug 2019 12:51:09 +0300
changeset 553509 fce0b326cd318bf435d4e4c54ae331618059e073
parent 553445 ea3eebc73ccb07ce574044b7063b95a309fc0933 (current diff)
parent 553508 0f58e37830b0cfa0f34499d84fdc9ae264b312f2 (diff)
child 553510 d1df6caf3b1da7f711da79e18b04cb3cccf07bd2
child 553519 2093b28e6aa976e6e559217dcd45bf865aa5e7be
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
fce0b326cd31 / 70.0a1 / 20190824095144 / files
nightly linux64
fce0b326cd31 / 70.0a1 / 20190824095144 / files
nightly mac
fce0b326cd31 / 70.0a1 / 20190824095144 / files
nightly win32
fce0b326cd31 / 70.0a1 / 20190824095144 / files
nightly win64
fce0b326cd31 / 70.0a1 / 20190824095144 / 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 inbound to mozilla-central. a=merge
testing/web-platform/meta/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.html.ini
testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.html
testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.worker.js
testing/web-platform/tests/html/webappapis/animation-frames/idlharness.html
testing/web-platform/tests/web-nfc/NFCReader-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_mediaType-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_recordType_empty-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_recordType_json-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_recordType_opaque-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_recordType_text-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_recordType_url-manual.https.html
testing/web-platform/tests/web-nfc/NFCReader_options_url-manual.https.html
testing/web-platform/tests/web-nfc/NFCWriter_push_signal-manual.https.html
testing/web-platform/tests/web-nfc/nfc_hw_disabled-manual.https.html
testing/web-platform/tests/web-nfc/nfc_push_ArrayBuffer-manual.https.html
testing/web-platform/tests/web-nfc/nfc_push_DOMString-manual.https.html
testing/web-platform/tests/web-nfc/resources/nfc_help.js
--- a/accessible/tests/mochitest/treeview.js
+++ b/accessible/tests/mochitest/treeview.js
@@ -158,19 +158,16 @@ nsTreeView.prototype = {
     data.colsText[aCol.id] = aValue;
   },
   setCellValue: function setCellValue(aRow, aCol, aValue) {
     var data = this.getDataForIndex(aRow);
     data.value = aValue;
 
     this.mTree.invalidateCell(aRow, aCol);
   },
-  performAction: function performAction(aAction) {},
-  performActionOnRow: function performActionOnRow(aAction, aRow) {},
-  performActionOnCell: function performActionOnCell(aAction, aRow, aCol) {},
 
   // ////////////////////////////////////////////////////////////////////////////
   // public implementation
 
   appendItem: function appendItem(aText) {
     this.mData.push(new treeItem(aText));
   },
 
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -78,29 +78,16 @@ pageInfoTreeView.prototype = {
   clear() {
     if (this.tree) {
       this.tree.rowCountChanged(0, -this.rows);
     }
     this.rows = 0;
     this.data = [];
   },
 
-  handleCopy(row) {
-    return row < 0 || this.copycol < 0
-      ? ""
-      : this.data[row][this.copycol] || "";
-  },
-
-  performActionOnRow(action, row) {
-    if (action == "copy") {
-      var data = this.handleCopy(row);
-      this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
-    }
-  },
-
   onPageMediaSort(columnname) {
     var tree = document.getElementById(this.treeid);
     var treecol = tree.columns.getNamedColumn(columnname);
 
     this.sortdir = gTreeUtils.sort(
       tree,
       this,
       this.data,
@@ -165,18 +152,16 @@ pageInfoTreeView.prototype = {
   getCellValue(row, column) {},
   toggleOpenState(index) {},
   cycleHeader(col) {},
   selectionChanged() {},
   cycleCell(row, column) {},
   isEditable(row, column) {
     return false;
   },
-  performAction(action) {},
-  performActionOnCell(action, row, column) {},
 };
 
 // mmm, yummy. global variables.
 var gDocInfo = null;
 var gImageElement = null;
 
 // column number to help using the data array
 const COL_IMAGE_ADDRESS = 0;
@@ -279,29 +264,16 @@ var loadContextInfo = Services.loadConte
 var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false);
 
 const nsICookiePermission = Ci.nsICookiePermission;
 const nsIPermissionManager = Ci.nsIPermissionManager;
 
 const nsICertificateDialogs = Ci.nsICertificateDialogs;
 const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1";
 
-// clipboard helper
-function getClipboardHelper() {
-  try {
-    return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
-      Ci.nsIClipboardHelper
-    );
-  } catch (e) {
-    // do nothing, later code will handle the error
-    return null;
-  }
-}
-const gClipboardHelper = getClipboardHelper();
-
 // namespaces, don't need all of these yet...
 const XLinkNS = "http://www.w3.org/1999/xlink";
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const XMLNS = "http://www.w3.org/XML/1998/namespace";
 const XHTMLNS = "http://www.w3.org/1999/xhtml";
 const XHTML2NS = "http://www.w3.org/2002/06/xhtml2";
 
 const XHTMLNSre = "^http://www.w3.org/1999/xhtml$";
@@ -1183,50 +1155,16 @@ function formatDate(datestr, unknown) {
 
   const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
     dateStyle: "long",
     timeStyle: "long",
   });
   return dateTimeFormatter.format(date);
 }
 
-function doCopy() {
-  if (!gClipboardHelper) {
-    return;
-  }
-
-  var elem = document.commandDispatcher.focusedElement;
-
-  if (elem && elem.localName == "tree") {
-    var view = elem.view;
-    var selection = view.selection;
-    var text = [],
-      tmp = "";
-    var min = {},
-      max = {};
-
-    var count = selection.getRangeCount();
-
-    for (var i = 0; i < count; i++) {
-      selection.getRangeAt(i, min, max);
-
-      for (var row = min.value; row <= max.value; row++) {
-        view.performActionOnRow("copy", row);
-
-        tmp = elem.getAttribute("copybuffer");
-        if (tmp) {
-          text.push(tmp);
-        }
-        elem.removeAttribute("copybuffer");
-      }
-    }
-    gClipboardHelper.copyString(text.join("\n"));
-  }
-}
-
 function doSelectAllMedia() {
   var tree = document.getElementById("imagetree");
 
   if (tree) {
     tree.view.selection.selectAll();
   }
 }
 
--- a/browser/base/content/pageinfo/pageInfo.xul
+++ b/browser/base/content/pageinfo/pageInfo.xul
@@ -42,36 +42,33 @@
   <stringbundleset id="pageinfobundleset">
     <stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/>
     <stringbundle id="browserBundle" src="chrome://browser/locale/browser.properties"/>
   </stringbundleset>
 
   <commandset id="pageInfoCommandSet">
     <command id="cmd_close"     oncommand="window.close();"/>
     <command id="cmd_help"      oncommand="doHelpButton();"/>
-    <command id="cmd_copy"      oncommand="doCopy();"/>
     <command id="cmd_selectall" oncommand="doSelectAll();"/>
   </commandset>
 
   <keyset id="pageInfoKeySet">
     <key data-l10n-id="close-dialog" data-l10n-attrs="key" modifiers="accel" command="cmd_close"/>
     <key keycode="VK_ESCAPE" command="cmd_close"/>
 #ifdef XP_MACOSX
     <key key="." modifiers="meta"  command="cmd_close"/>
 #else
     <key keycode="VK_F1" command="cmd_help"/>
 #endif
-    <key data-l10n-id="copy"       data-l10n-attrs="key" modifiers="accel" command="cmd_copy"/>
     <key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="accel" command="cmd_selectall"/>
     <key data-l10n-id="select-all" data-l10n-attrs="key" modifiers="alt"   command="cmd_selectall"/>
   </keyset>
 
   <menupopup id="picontext">
     <menuitem id="menu_selectall" data-l10n-id="menu-select-all" command="cmd_selectall"/>
-    <menuitem id="menu_copy"      data-l10n-id="menu-copy"      command="cmd_copy"/>
   </menupopup>
 
   <vbox id="topBar">
     <radiogroup id="viewGroup" class="chromeclass-toolbar" orient="horizontal">
       <radio id="generalTab"  data-l10n-id="general-tab"
            oncommand="showTab('general');"/>
       <radio id="mediaTab"    data-l10n-id="media-tab"
            oncommand="showTab('media');" hidden="true"/>
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -1825,12 +1825,9 @@ PlacesTreeView.prototype = {
       }
 
       this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
     }
   },
 
   selectionChanged() {},
   cycleCell(aRow, aColumn) {},
-  performAction(aAction) {},
-  performActionOnRow(aAction, aRow) {},
-  performActionOnCell(aAction, aRow, aColumn) {},
 };
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -787,12 +787,9 @@ EngineView.prototype = {
         .editKeyword(this._engineStore.engines[index], value)
         .then(valid => {
           if (!valid) {
             document.getElementById("engineList").startEditing(index, column);
           }
         });
     }
   },
-  performAction(action) {},
-  performActionOnRow(action, index) {},
-  performActionOnCell(action, index, column) {},
 };
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -459,14 +459,12 @@ var treeView = {
       return gTreeData[idx].src || null;
     }
     return null;
   },
 
   cycleHeader(column) {},
   cycleCell(idx, column) {},
   selectionChanged() {},
-  performAction(action) {},
-  performActionOnCell(action, index, column) {},
   getColumnProperties(column) {
     return "";
   },
 };
--- a/browser/locales/en-US/browser/pageInfo.ftl
+++ b/browser/locales/en-US/browser/pageInfo.ftl
@@ -1,21 +1,15 @@
 # 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/. --
 
 page-info-window =
     .style = width: 600px; min-height: 550px;
 
-copy =
-    .key = C
-menu-copy =
-    .label = Copy
-    .accesskey = C
-
 select-all =
     .key = A
 menu-select-all =
     .label = Select All
     .accesskey = A
 
 close-dialog =
     .key = w
--- a/dom/webidl/TreeView.webidl
+++ b/dom/webidl/TreeView.webidl
@@ -179,26 +179,9 @@ interface TreeView
   [Throws]
   void setCellValue(long row, TreeColumn column, DOMString value);
 
   /**
    * setCellText is called when the contents of the cell have been edited by the user.
    */
   [Throws]
   void setCellText(long row, TreeColumn column, DOMString value);
-
-  /**
-   * A command API that can be used to invoke commands on the selection.  The tree
-   * will automatically invoke this method when certain keys are pressed.  For example,
-   * when the DEL key is pressed, performAction will be called with the "delete" string.
-   */
-  void performAction(DOMString action);
-
-  /**
-   * A command API that can be used to invoke commands on a specific row.
-   */
-  void performActionOnRow(DOMString action, long row);
-
-  /**
-   * A command API that can be used to invoke commands on a specific cell.
-   */
-  void performActionOnCell(DOMString action, long row, TreeColumn column);
 };
--- a/layout/xul/tree/nsITreeView.idl
+++ b/layout/xul/tree/nsITreeView.idl
@@ -165,26 +165,9 @@ interface nsITreeView : nsISupports
    * This method is only called for columns of type other than |text|.
    */
   void setCellValue(in long row, in TreeColumn col, in AString value);
 
   /**
    * setCellText is called when the contents of the cell have been edited by the user.
    */
   void setCellText(in long row, in TreeColumn col, in AString value);
-
-  /**
-   * A command API that can be used to invoke commands on the selection.  The tree
-   * will automatically invoke this method when certain keys are pressed.  For example,
-   * when the DEL key is pressed, performAction will be called with the "delete" string.
-   */
-  void performAction(in wstring action);
-
-  /**
-   * A command API that can be used to invoke commands on a specific row.
-   */
-  void performActionOnRow(in wstring action, in long row);
-
-  /**
-   * A command API that can be used to invoke commands on a specific cell.
-   */
-  void performActionOnCell(in wstring action, in long row, in TreeColumn col);
 };
--- a/layout/xul/tree/nsTreeContentView.cpp
+++ b/layout/xul/tree/nsTreeContentView.cpp
@@ -684,30 +684,16 @@ nsTreeContentView::SetCellText(int32_t a
                                const nsAString& aValue) {
   NS_ENSURE_ARG(aCol);
 
   ErrorResult rv;
   SetCellText(aRow, *aCol, aValue, rv);
   return rv.StealNSResult();
 }
 
-NS_IMETHODIMP
-nsTreeContentView::PerformAction(const char16_t* aAction) { return NS_OK; }
-
-NS_IMETHODIMP
-nsTreeContentView::PerformActionOnRow(const char16_t* aAction, int32_t aRow) {
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTreeContentView::PerformActionOnCell(const char16_t* aAction, int32_t aRow,
-                                       nsTreeColumn* aCol) {
-  return NS_OK;
-}
-
 Element* nsTreeContentView::GetItemAtIndex(int32_t aIndex,
                                            ErrorResult& aError) {
   if (!IsValidRowIndex(aIndex)) {
     aError.Throw(NS_ERROR_INVALID_ARG);
     return nullptr;
   }
 
   return mRows[aIndex]->mContent;
--- a/layout/xul/tree/nsTreeContentView.h
+++ b/layout/xul/tree/nsTreeContentView.h
@@ -82,20 +82,16 @@ class nsTreeContentView final : public n
   void SelectionChanged() {}
   void CycleCell(int32_t aRow, nsTreeColumn& aColumn) {}
   bool IsEditable(int32_t aRow, nsTreeColumn& aColumn,
                   mozilla::ErrorResult& aError);
   void SetCellValue(int32_t aRow, nsTreeColumn& aColumn,
                     const nsAString& aValue, mozilla::ErrorResult& aError);
   void SetCellText(int32_t aRow, nsTreeColumn& aColumn, const nsAString& aText,
                    mozilla::ErrorResult& aError);
-  void PerformAction(const nsAString& aAction) {}
-  void PerformActionOnRow(const nsAString& aAction, int32_t aRow) {}
-  void PerformActionOnCell(const nsAString& aAction, int32_t aRow,
-                           nsTreeColumn& aColumn) {}
   Element* GetItemAtIndex(int32_t aRow, mozilla::ErrorResult& aError);
   int32_t GetIndexOfItem(Element* aItem);
 
   NS_DECL_NSITREEVIEW
 
   // nsIDocumentObserver
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
--- a/media/mtransport/third_party/nICEr/src/stun/stun.h
+++ b/media/mtransport/third_party/nICEr/src/stun/stun.h
@@ -36,17 +36,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 #ifdef WIN32
 #include <winsock2.h>
 #else
 #include <sys/param.h>
 #include <sys/socket.h>
 #ifndef LINUX
 #include <net/if.h>
-#if !defined(__OpenBSD__) && !defined(__NetBSD__)
+#ifdef DARWIN
 #include <net/if_var.h>
 #endif
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #else
 #include <linux/if.h>
 #endif
 #ifndef BSD
--- a/security/manager/pki/nsASN1Tree.cpp
+++ b/security/manager/pki/nsASN1Tree.cpp
@@ -306,27 +306,16 @@ nsNSSASN1Tree::SetCellValue(int32_t, nsT
 }
 
 NS_IMETHODIMP
 nsNSSASN1Tree::SetCellText(int32_t, nsTreeColumn*, const nsAString&) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSASN1Tree::PerformAction(const char16_t*) { return NS_OK; }
-
-NS_IMETHODIMP
-nsNSSASN1Tree::PerformActionOnRow(const char16_t*, int32_t) { return NS_OK; }
-
-NS_IMETHODIMP
-nsNSSASN1Tree::PerformActionOnCell(const char16_t*, int32_t, nsTreeColumn*) {
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNSSASN1Tree::CanDrop(int32_t, int32_t, mozilla::dom::DataTransfer*,
                        bool* _retval) {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/security/manager/ssl/nsCertTree.cpp
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -1041,30 +1041,16 @@ nsCertTree::SetCellValue(int32_t row, ns
 }
 
 NS_IMETHODIMP
 nsCertTree::SetCellText(int32_t row, nsTreeColumn* col,
                         const nsAString& value) {
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCertTree::PerformAction(const char16_t* action) { return NS_OK; }
-
-NS_IMETHODIMP
-nsCertTree::PerformActionOnRow(const char16_t* action, int32_t row) {
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsCertTree::PerformActionOnCell(const char16_t* action, int32_t row,
-                                nsTreeColumn* col) {
-  return NS_OK;
-}
-
 #ifdef DEBUG_CERT_TREE
 void nsCertTree::dumpMap() {
   for (int i = 0; i < mNumOrgs; i++) {
     nsAutoString org(mTreeArray[i].orgName);
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("ORG[%s]", NS_LossyConvertUTF16toASCII(org).get()));
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("OPEN[%d]", mTreeArray[i].open));
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-box/parsing/padding-valid.html.ini
@@ -0,0 +1,4 @@
+[padding-valid.html]
+  [e.style['padding-right'\] = "calc(2em + 3%)" should set the property value]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-paint-api/custom-property-animation-on-main-thread.https.html.ini
@@ -0,0 +1,2 @@
+[custom-property-animation-on-main-thread.https.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-paint-api/one-custom-property-animation.https.html.ini
@@ -0,0 +1,2 @@
+[one-custom-property-animation.https.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-paint-api/two-custom-property-animation.https.html.ini
@@ -0,0 +1,2 @@
+[two-custom-property-animation.https.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-position/animations/bottom-interpolation.html.ini
@@ -0,0 +1,4 @@
+[bottom-interpolation.html]
+  [bottom-interpolation]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-position/animations/left-interpolation.html.ini
@@ -0,0 +1,4 @@
+[left-interpolation.html]
+  [left-interpolation]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-position/animations/right-interpolation.html.ini
@@ -0,0 +1,4 @@
+[right-interpolation.html]
+  [right-interpolation]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-position/animations/top-interpolation.html.ini
@@ -0,0 +1,4 @@
+[top-interpolation.html]
+  [top-interpolation]
+    expected: FAIL
+
--- a/testing/web-platform/meta/css/css-transitions/CSSTransition-effect.tentative.html.ini
+++ b/testing/web-platform/meta/css/css-transitions/CSSTransition-effect.tentative.html.ini
@@ -1,7 +1,4 @@
 [CSSTransition-effect.tentative.html]
   [After setting a transition's effect to null, a new transition can be started]
     expected: FAIL
 
-  [After setting a transition's effect to null, it should be possible to interrupt that transition]
-    expected: FAIL
-
--- a/testing/web-platform/meta/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.html.ini
+++ b/testing/web-platform/meta/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.html.ini
@@ -9,8 +9,14 @@
     expected: FAIL
 
   [Parsing a calc with incompatible units throws a SyntaxError]
     expected: FAIL
 
   [Parsing ignores surrounding spaces]
     expected: FAIL
 
+  [Parsing max() is successful]
+    expected: FAIL
+
+  [Parsing min() is successful]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-typed-om/width-by-max-px-em.html.ini
@@ -0,0 +1,2 @@
+[width-by-max-px-em.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-typed-om/width-by-min-px-em.html.ini
@@ -0,0 +1,2 @@
+[width-by-min-px-em.html]
+  expected: FAIL
--- a/testing/web-platform/meta/css/css-ui/appearance-textfield-001.html.ini
+++ b/testing/web-platform/meta/css/css-ui/appearance-textfield-001.html.ini
@@ -1,7 +1,6 @@
 [appearance-textfield-001.html]
   expected:
     if (os == "win") and debug and not webrender and (processor == "x86"): [FAIL, PASS]
     if (os == "win") and debug and webrender: FAIL
     if (os == "win") and debug and not webrender and (processor == "x86_64"): [PASS, FAIL]
     if (os == "win") and (processor == "x86_64"): FAIL
-    if (os == "win") and not debug: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-values/max-length-percent-001.html.ini
@@ -0,0 +1,2 @@
+[max-length-percent-001.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-values/min-length-percent-001.html.ini
@@ -0,0 +1,2 @@
+[min-length-percent-001.html]
+  expected: FAIL
--- a/testing/web-platform/meta/element-timing/idlharness.window.js.ini
+++ b/testing/web-platform/meta/element-timing/idlharness.window.js.ini
@@ -42,8 +42,11 @@
     expected: FAIL
 
   [PerformanceElementTiming interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
   [PerformanceElementTiming interface: existence and properties of interface prototype object]
     expected: FAIL
 
+  [PerformanceElementTiming interface: operation toJSON()]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/element-timing/toJSON.html.ini
@@ -0,0 +1,4 @@
+[toJSON.html]
+  [Test toJSON() in PerformanceElementTiming.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/event-timing/idlharness.any.js.ini
+++ b/testing/web-platform/meta/event-timing/idlharness.any.js.ini
@@ -45,16 +45,19 @@
     expected: FAIL
 
   [EventCounts interface object length]
     expected: FAIL
 
   [EventCounts interface object name]
     expected: FAIL
 
+  [PerformanceEventTiming interface: operation toJSON()]
+    expected: FAIL
+
 
 [idlharness.any.worker.html]
   [Performance interface: performance must inherit property "eventCounts" with the proper type]
     expected: FAIL
 
   [Performance interface: attribute eventCounts]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/event-timing/toJSON.html.ini
@@ -0,0 +1,4 @@
+[toJSON.html]
+  [Test toJSON() in PerformanceEventTiming.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/html/cross-origin-opener-policy/popup-none.https.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener-policy/popup-none.https.html.ini
@@ -1,24 +1,12 @@
 [popup-none.https.html]
-  [none document opening popup to https://web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
-  [none document opening popup to https://web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
-  [none document opening popup to https://www1.web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
   [none document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
-  [none document opening popup to https://www1.web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
   [none document opening popup to https://web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [none document opening popup to https://web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
   [none document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
@@ -30,26 +18,20 @@
     expected: FAIL
 
   [none document opening popup to https://web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [none document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [none document opening popup to https://not-web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
   [none document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
   [none document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [none document opening popup to https://not-web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
   [none document opening popup to https://not-web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [none document opening popup to https://not-web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-origin-unsafe-allow-outgoing.https.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-origin-unsafe-allow-outgoing.https.html.ini
@@ -1,55 +1,34 @@
 [popup-same-origin-unsafe-allow-outgoing.https.html]
   [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
-  [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
   [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
-  [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
-    expected: FAIL
-
   [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
   [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
-  [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
   [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-origin_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
-  [same-origin_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
-  [same-origin_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-origin.https.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-origin.https.html.ini
@@ -39,17 +39,14 @@
     expected: FAIL
 
   [same-origin document opening popup to https://www1.web-platform.test:8443 with COOP: ""]
     expected: FAIL
 
   [same-origin document opening popup to https://web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-origin document opening popup to https://web-platform.test:8443 with COOP: "same-origin"]
-    expected: FAIL
-
   [same-origin document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-origin document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-site-unsafe-allow-outgoing.https.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-site-unsafe-allow-outgoing.https.html.ini
@@ -1,55 +1,31 @@
 [popup-same-site-unsafe-allow-outgoing.https.html]
-  [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
   [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
-    expected: FAIL
-
   [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
-  [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
-    expected: FAIL
-
   [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
   [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
-  [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
-  [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
-  [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: ""]
-    expected: FAIL
-
   [same-site_unsafe-allow-outgoing document opening popup to https://web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-site_unsafe-allow-outgoing document opening popup to https://not-web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
-  [same-site_unsafe-allow-outgoing document opening popup to https://www1.web-platform.test:8443 with COOP: "jibberish"]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-site.https.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener-policy/popup-same-site.https.html.ini
@@ -1,21 +1,15 @@
 [popup-same-site.https.html]
   [same-site document opening popup to https://not-web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
     expected: FAIL
 
   [same-site document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
-  [same-site document opening popup to https://www1.web-platform.test:8443 with COOP: "same-site"]
-    expected: FAIL
-
-  [same-site document opening popup to https://web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
-    expected: FAIL
-
   [same-site document opening popup to https://web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-site document opening popup to https://not-web-platform.test:8443 with COOP: "jibberish"]
     expected: FAIL
 
   [same-site document opening popup to https://web-platform.test:8443 with COOP: ""]
     expected: FAIL
@@ -42,14 +36,14 @@
     expected: FAIL
 
   [same-site document opening popup to https://www1.web-platform.test:8443 with COOP: "same-origin"]
     expected: FAIL
 
   [same-site document opening popup to https://not-web-platform.test:8443 with COOP: "same-site"]
     expected: FAIL
 
-  [same-site document opening popup to https://web-platform.test:8443 with COOP: "same-site"]
-    expected: FAIL
-
   [same-site document opening popup to https://web-platform.test:8443 with COOP: "same-site unsafe-allow-outgoing"]
     expected: FAIL
 
+  [same-site document opening popup to https://web-platform.test:8443 with COOP: "same-origin unsafe-allow-outgoing"]
+    expected: FAIL
+
--- a/testing/web-platform/meta/html/dom/interfaces.https.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.https.html.ini
@@ -1222,8 +1222,29 @@ prefs: [dom.security.featurePolicy.enabl
     expected: FAIL
 
   [SVGAElement interface: attribute username]
     expected: FAIL
 
   [SVGElement interface: attribute onformdata]
     expected: FAIL
 
+  [DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [DOMStringList interface: calling contains(DOMString) on location.ancestorOrigins with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [DOMStringList interface: location.ancestorOrigins must inherit property "contains(DOMString)" with the proper type]
+    expected: FAIL
+
+  [Stringification of location.ancestorOrigins]
+    expected: FAIL
+
+  [DOMStringList interface: location.ancestorOrigins must inherit property "item(unsigned long)" with the proper type]
+    expected: FAIL
+
+  [DOMStringList must be primary interface of location.ancestorOrigins]
+    expected: FAIL
+
+  [DOMStringList interface: location.ancestorOrigins must inherit property "length" with the proper type]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.html.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-[domstringlist-interface.html]
-  [DOMStringList must be primary interface of location.ancestorOrigins]
-    expected: FAIL
-
-  [Stringification of location.ancestorOrigins]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "length" with the proper type (0)]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "item" with the proper type (1)]
-    expected: FAIL
-
-  [DOMStringList interface: calling item(unsigned long) on location.ancestorOrigins with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "contains" with the proper type (2)]
-    expected: FAIL
-
-  [DOMStringList interface: calling contains(DOMString) on location.ancestorOrigins with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "length" with the proper type]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "item(unsigned long)" with the proper type]
-    expected: FAIL
-
-  [DOMStringList interface: location.ancestorOrigins must inherit property "contains(DOMString)" with the proper type]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html.ini
@@ -0,0 +1,4 @@
+[tabindex-getter.html]
+  [object.tabIndex should return 0 by default]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/largest-contentful-paint/first-letter-background.html.ini
@@ -0,0 +1,4 @@
+[first-letter-background.html]
+  [Largest Contentful Paint: first-letter is observable.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/largest-contentful-paint/idlharness.html.ini
+++ b/testing/web-platform/meta/largest-contentful-paint/idlharness.html.ini
@@ -57,8 +57,17 @@
     expected: FAIL
 
   [LargestContentfulPaint interface: attribute loadTime]
     expected: FAIL
 
   [LargestContentfulPaint interface: attribute element]
     expected: FAIL
 
+  [LargestContentfulPaint interface: default toJSON operation on lcp]
+    expected: FAIL
+
+  [LargestContentfulPaint interface: lcp must inherit property "toJSON()" with the proper type]
+    expected: FAIL
+
+  [LargestContentfulPaint interface: operation toJSON()]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/largest-contentful-paint/toJSON.html.ini
@@ -0,0 +1,4 @@
+[toJSON.html]
+  [Test toJSON() in LargestContentfulPaint.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/layout-instability/idlharness.window.js.ini
+++ b/testing/web-platform/meta/layout-instability/idlharness.window.js.ini
@@ -21,8 +21,11 @@
     expected: FAIL
 
   [LayoutShift interface: attribute hadRecentInput]
     expected: FAIL
 
   [LayoutShift interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
+  [LayoutShift interface: operation toJSON()]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/layout-instability/toJSON.html.ini
@@ -0,0 +1,4 @@
+[toJSON.html]
+  [Test toJSON() in LayoutShift.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,2 +1,2 @@
-local: 440ff4a33138cd1b827cce60f6a3ec639942ac07
-upstream: 8194a35b1626cb99346d3b2d008f83998194c2af
+local: 88475cf027ba15ec10b5edd273b443b08c57b613
+upstream: c1e89362cb351be0ea9239e38cf5830e104ba1f4
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html.ini
@@ -0,0 +1,4 @@
+[focus-tabindex-order-shadow-zero-host-negative.html]
+  [Order when all tabindex=0 except for host, which has tabindex=-1]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html.ini
@@ -0,0 +1,4 @@
+[focus-tabindex-order-shadow-zero-host-one.html]
+  [Order when all tabindex=0 except for host, which has tabindex=1]
+    expected: FAIL
+
--- a/testing/web-platform/meta/streams/readable-streams/patched-global.any.js.ini
+++ b/testing/web-platform/meta/streams/readable-streams/patched-global.any.js.ini
@@ -1,24 +1,39 @@
 [patched-global.any.serviceworker.html]
   [ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods]
     expected: FAIL
 
+  [pipeTo() should not call Promise.prototype.then()]
+    expected: FAIL
+
 
 [patched-global.any.sharedworker.html]
   [ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods]
     expected: FAIL
 
+  [pipeTo() should not call Promise.prototype.then()]
+    expected: FAIL
+
 
 [patched-global.any.worker.html]
   [ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods]
     expected: FAIL
 
+  [pipeTo() should not call Promise.prototype.then()]
+    expected: FAIL
+
 
 [patched-global.any.html]
   [ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods]
     expected: FAIL
 
+  [pipeTo() should not call Promise.prototype.then()]
+    expected: FAIL
+
 
 [patched-global.any.js]
   [ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods]
     expected: FAIL
 
+  [pipeTo() should not call Promise.prototype.then()]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/svg/animations/additive-type-by-animation.html.ini
@@ -0,0 +1,4 @@
+[additive-type-by-animation.html]
+  [This by animation for all XML property types]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/svg/animations/animate-color-transparent.html.ini
@@ -0,0 +1,4 @@
+[animate-color-transparent.html]
+  [Tests that 'transparent' is treated as a valid color.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/svg/animations/animate-css-xml-attributeType.html.ini
@@ -0,0 +1,4 @@
+[animate-css-xml-attributeType.html]
+  [Tests that XML and CSS attributeTypes can be switched between.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/user-timing/measure_exception.html.ini
@@ -0,0 +1,7 @@
+[measure_exception.html]
+  [Invocation of performance.measure("Exception9", {"start": 1, "duration": 2, "end": 3}) should throw TypeError Exception.]
+    expected: FAIL
+
+  [Invocation of performance.measure("Exception8", {"detail": "non-empty"}) should throw TypeError Exception.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/web-nfc/NFCReader.https.html.ini
+++ b/testing/web-platform/meta/web-nfc/NFCReader.https.html.ini
@@ -3,8 +3,29 @@
     expected: FAIL
 
   [Test that NFCReader.start fails if NFCReaderOptions.url is invalid.]
     expected: FAIL
 
   [Test that NFCReader.start fails if NFCReaderOptions.url is missing components.]
     expected: FAIL
 
+  [Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL with '*' wildcard character in the beginning of path component followed by subpath.]
+    expected: FAIL
+
+  [Test that nfc watch success if NFC HW is enabled.]
+    expected: FAIL
+
+  [Test that NFCReader.start succeeds if NFCReaderOptions.url is empty.]
+    expected: FAIL
+
+  [Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL with '*' wildcard character in path.]
+    expected: FAIL
+
+  [NFCReader.start should fail if NFC HW is not supported.]
+    expected: FAIL
+
+  [Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL.]
+    expected: FAIL
+
+  [NFCReader.start should fail if NFC HW is disabled.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/web-nfc/NFCReader_options.https.html.ini
@@ -0,0 +1,79 @@
+[NFCReader_options.https.html]
+  [Test that write and read data succeed when NFCReaderOptions's recordType is set to 'opaque'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions's recordType is set to 'json'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions's recordType is set to 'text'.]
+    expected: FAIL
+
+  [Test that the mediaType of NFCReaderOptions filters relevant data sources correctly.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions's recordType is set to 'url'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions's recordType is set to 'empty'.]
+    expected: FAIL
+
+  [Test that the url of NFCReaderOptions filters relevant data sources correctly.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions' recordType is set to 'url'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions' recordType is set to 'opaque'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions' recordType is set to 'empty'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions' recordType is set to 'json'.]
+    expected: FAIL
+
+  [Test that write and read data succeed when NFCReaderOptions' recordType is set to 'text'.]
+    expected: FAIL
+
+  [Test that the compatibility of NFCReaderOptions filters relevant data sources correctly.]
+    expected: FAIL
+
+  [Test that filtering 'json' record from different messages correctly with NFCReaderOptions' recordType is set to 'json'.]
+    expected: FAIL
+
+  [Test that reading data succeed when NFCReaderOptions' recordType is set to 'json'.]
+    expected: FAIL
+
+  [Test that filtering 'opaque' record from different messages correctly with NFCReaderOptions' recordType is set to 'opaque'.]
+    expected: FAIL
+
+  [Test that filtering 'text' record from different messages correctly with NFCReaderOptions' recordType is set to 'text'.]
+    expected: FAIL
+
+  [Test that reading data succeed when NFCReaderOptions' recordType is set to 'url'.]
+    expected: FAIL
+
+  [Test that reading data succeed when NFCReaderOptions' recordType is set to 'text'.]
+    expected: FAIL
+
+  [Test that filtering 'text' record from different messages correctly with NFCReaderOptions' url set.]
+    expected: FAIL
+
+  [Test that filtering 'text' record from different messages correctly with NFCReaderOptions' compatibility set.]
+    expected: FAIL
+
+  [Test that reading data succeed when NFCReaderOptions' recordType is set to 'opaque'.]
+    expected: FAIL
+
+  [Test that filtering 'url' record from different messages correctly with NFCReaderOptions' recordType is set to 'url'.]
+    expected: FAIL
+
+  [Test that filtering 'opaque' record from different messages correctly with NFCReaderOptions' mediaType set.]
+    expected: FAIL
+
+  [Test that filtering 'empty' record from different messages correctly with NFCReaderOptions' recordType is set to 'empty'.]
+    expected: FAIL
+
+  [Test that reading data succeed when NFCReaderOptions' recordType is set to 'empty'.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/web-nfc/NFCWriter_push.https.html.ini
+++ b/testing/web-platform/meta/web-nfc/NFCWriter_push.https.html.ini
@@ -45,8 +45,44 @@
     expected: FAIL
 
   [NFCWriter.push should fail if abort push request before push happends.]
     expected: FAIL
 
   [Test that promise is rejected with TypeError if NDEFMessageSource contains non-string url.]
     expected: FAIL
 
+  [Check that provided NFCPushOptions values are correctly converted.]
+    expected: FAIL
+
+  [Check that default NFCPushOptions values are correctly set.]
+    expected: FAIL
+
+  [Test that NFCWriter.push succeeds when message is ArrayBuffer.]
+    expected: FAIL
+
+  [Test that NFCWriter.push succeeds when message is DOMString.]
+    expected: FAIL
+
+  [NFCWriter.push should fail when NFC HW is not supported.]
+    expected: FAIL
+
+  [NFCWriter.push NDEFMessage containing text, json, opaque and url records with default NFCPushOptions.]
+    expected: FAIL
+
+  [Synchronously signaled abort.]
+    expected: FAIL
+
+  [NFCWriter.push should succeed when NFC HW is enabled]
+    expected: FAIL
+
+  [NFCWriter.push with 'empty' record should succeed.]
+    expected: FAIL
+
+  [Test that promise is rejected with SyntaxError if NDEFMessageSource contains invalid records.]
+    expected: FAIL
+
+  [NFCWriter.push should fail when NFC HW is disabled.]
+    expected: FAIL
+
+  [NFCReader.start should fail if NFC HW is disabled.]
+    expected: FAIL
+
--- a/testing/web-platform/tests/.azure-pipelines.yml
+++ b/testing/web-platform/tests/.azure-pipelines.yml
@@ -1,27 +1,35 @@
 # This is the configuration file for Azure Pipelines, used to run tests on
 # macOS. Documentation to help understand this setup:
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema
+# https://docs.microsoft.com/en-us/azure/devops/pipelines/build/triggers
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/multiple-phases
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables
 # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/index
 #
 # In addition to this configuration file, some setup in the Azure DevOps
 # project is required:
 #  - The "Build pull requests from forks of this repository" setting must be
 #    enabled: https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github#validate-contributions-from-forks
-#  - A scheduled build needs to be set up for one of the epochs/* branches.
 #  - Self-hosted agents for Windows 10 are used:
 #    - 'Hosted Windows Client' is the latest Windows 10
 #    - 'Hosted Windows Client Next' is Windows 10 Insider Preview
 #    Documention for the setup of these agents:
 #    https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows
 
+schedules:
+- cron: "15 */6 * * *"
+  displayName: Every six hours
+  branches:
+    include:
+    - epochs/six_hourly
+  always: true
+
 jobs:
 # The affected tests jobs are unconditional for speed, as most PRs have one or
 # more affected tests: https://github.com/web-platform-tests/wpt/issues/13936.
 - job: affected_safari_preview
   displayName: 'affected tests: Safari Technology Preview'
   condition: eq(variables['Build.Reason'], 'PullRequest')
   pool:
     vmImage: 'macOS-10.13'
@@ -229,21 +237,21 @@ jobs:
     displayName: 'Publish results'
     inputs:
       artifactName: 'infrastructure'
     condition: always()
   - template: tools/ci/azure/cleanup_win10.yml
 
 # All `./wpt run` tests are run from epochs/* branches on a schedule. See
 # documentation at the top of this file for required setup.
-- job: results_edge
+- job: results_edge_dev
   displayName: 'all tests: Edge Dev'
   condition: |
     or(eq(variables['Build.Reason'], 'Schedule'),
-       and(eq(variables['Build.Reason'], 'Manual'), variables['run_all_edge']))
+       and(eq(variables['Build.Reason'], 'Manual'), variables['run_all_edge_dev']))
   # There are 12 agents in the pool, but use more jobs so that each takes <1h.
   strategy:
     parallel: 20
   timeoutInMinutes: 360
   pool:
     name: 'Hosted Windows Client'
   steps:
   - template: tools/ci/azure/system_info.yml
@@ -258,28 +266,28 @@ jobs:
       channel: dev
   - template: tools/ci/azure/update_hosts.yml
   - template: tools/ci/azure/update_manifest.yml
   - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-tbpl - --log-tbpl-level info --channel dev edgechromium
     displayName: 'Run tests (Edge Dev)'
   - task: PublishBuildArtifacts@1
     displayName: 'Publish results'
     inputs:
-      artifactName: 'edge-results'
+      artifactName: 'edge-dev-results'
   - template: tools/ci/azure/cleanup_win10.yml
 - template: tools/ci/azure/fyi_hook.yml
   parameters:
-    dependsOn: results_edge
-    artifactName: edge-results
+    dependsOn: results_edge_dev
+    artifactName: edge-dev-results
 
 - job: results_edge_canary
   displayName: 'all tests: Edge Canary'
   condition: |
     or(eq(variables['Build.Reason'], 'Schedule'),
-       and(eq(variables['Build.Reason'], 'Manual'), variables['run_all_edge']))
+       and(eq(variables['Build.Reason'], 'Manual'), variables['run_all_edge_canary']))
   # There are 12 agents in the pool, but use more jobs so that each takes <1h.
   strategy:
     parallel: 20
   timeoutInMinutes: 360
   pool:
     name: 'Hosted Windows Client'
   steps:
   - template: tools/ci/azure/system_info.yml
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.js
@@ -14,33 +14,35 @@ self.addEventListener('install', (event)
       // If the worker enters the "redundant" state, the UA may terminate it
       // before all tests have been reported to the client. Stifle errors in
       // order to avoid this and ensure all tests are consistently reported.
     } catch (err) {}
   })());
 });
 
 // Resolves when the service worker receives the 'activate' event.
-const kServiceWorkerActivatedPromise = new Promise(resolve => {
+const kServiceWorkerActivatedPromise = new Promise((resolve) => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
+const kCookieChangeReceivedPromise = new Promise((resolve) => {
+  self.addEventListener('cookiechange', (event) => {
+    resolve(event);
+  });
+});
+
 promise_test(async testCase => {
   await kServiceWorkerActivatedPromise;
 
-  const cookie_change_received_promise = new Promise((resolve) => {
-    self.oncookiechange = (event) => { resolve(event); };
-  });
-
   await cookieStore.set('cookie-name', 'cookie-value');
   testCase.add_cleanup(async () => {
     await cookieStore.delete('cookie-name');
   });
 
-  const event = await cookie_change_received_promise;
+  const event = await kCookieChangeReceivedPromise;
   assert_equals(event.type, 'cookiechange');
   assert_equals(event.changed.length, 1);
   assert_equals(event.changed[0].name, 'cookie-name');
   assert_equals(event.changed[0].value, 'cookie-value');
   assert_equals(event.deleted.length, 0);
   assert_true(event instanceof ExtendableCookieChangeEvent);
   assert_true(event instanceof ExtendableEvent);
 }, 'cookiechange dispatched with cookie change that matches subscription ' +
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html
@@ -6,17 +6,17 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
 (async () => {
   const scope = 'scope';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_subscriptions_eventhandler_attribute.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
 </script>
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js
@@ -14,38 +14,38 @@ self.addEventListener('install', (event)
       // If the worker enters the "redundant" state, the UA may terminate it
       // before all tests have been reported to the client. Stifle errors in
       // order to avoid this and ensure all tests are consistently reported.
     } catch (err) {}
   })());
 });
 
 // Resolves when the service worker receives the 'activate' event.
-const kServiceWorkerActivatedPromise = new Promise(resolve => {
+const kServiceWorkerActivatedPromise = new Promise((resolve) => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
+const kCookieChangeReceivedPromise = new Promise((resolve) => {
+  self.addEventListener('cookiechange', (event) => {
+    resolve(event);
+  });
+});
+
 promise_test(async testCase => {
   await kServiceWorkerActivatedPromise;
 
-  const cookie_change_received_promise = new Promise((resolve) => {
-    self.addEventListener('cookiechange', (event) => {
-      resolve(event);
-    });
-  });
-
   await cookieStore.set('another-cookie-name', 'cookie-value');
   testCase.add_cleanup(async () => {
     await cookieStore.delete('another-cookie-name');
   });
   await cookieStore.set('cookie-name', 'cookie-value');
   testCase.add_cleanup(async () => {
     await cookieStore.delete('cookie-name');
   });
 
-  const event = await cookie_change_received_promise;
+  const event = await kCookieChangeReceivedPromise;
   assert_equals(event.type, 'cookiechange');
   assert_equals(event.changed.length, 1);
   assert_equals(event.changed[0].name, 'cookie-name');
   assert_equals(event.changed[0].value, 'cookie-value');
 }, 'cookiechange not dispatched for change that does not match subscription');
 
 done();
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.tentative.https.html
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.tentative.https.html
@@ -5,17 +5,17 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
 (async () => {
   const scope = 'scope';
 
-  let registration = await navigator.serviceWorker.getRegistration(scope);
-  if (registration)
-    await registration.unregister();
-  registration = await navigator.serviceWorker.register(
+  const registration = await navigator.serviceWorker.register(
       'serviceworker_cookieStore_subscriptions_mismatch.js', {scope});
+  add_completion_callback(() => {
+    registration.unregister();
+  });
 
   fetch_tests_from_worker(registration.installing);
 })();
 </script>
--- a/testing/web-platform/tests/css/css-box/parsing/padding-invalid.html
+++ b/testing/web-platform/tests/css/css-box/parsing/padding-invalid.html
@@ -1,16 +1,16 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS basic box model: parsing padding with invalid values</title>
-<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-box-3/#propdef-padding">
-<meta name="assert" content="padding supports only the grammar '<length>{1,4}'.">
+<meta name="assert" content="padding supports only the grammar '<length-percentage>{1,4}'.">
+<meta name="assert" content="Negative values are invalid.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_invalid_value("padding", "auto");
 test_invalid_value("padding", "available");
@@ -18,17 +18,12 @@ test_invalid_value("padding", "10px bord
 test_invalid_value("padding", "calc(2em + 3ex) auto");
 test_invalid_value("padding", "10px 20px 30px 40px 50px");
 
 test_invalid_value("padding-top", "auto");
 test_invalid_value("padding-bottom", "10px 20px calc(2em + 3ex) auto");
 test_invalid_value("padding-bottom-left", "10px 20px");
 
 test_invalid_value("padding-top", "-10px");
-
-// The following are not yet rejected by browsers:
-test_invalid_value("padding", "20%");
-test_invalid_value("padding", "10px 20% 30% 40px");
-test_invalid_value("padding-right", "20%");
-test_invalid_value("padding-right", "calc(2em + 3%)");
+test_invalid_value("padding-right", "-20%");
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/css/css-box/parsing/padding-valid.html
+++ b/testing/web-platform/tests/css/css-box/parsing/padding-valid.html
@@ -1,25 +1,29 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS basic box model: parsing padding with valid values</title>
-<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-box-3/#propdef-padding">
-<meta name="assert" content="padding supports the full grammar '<length>{1,4}'.">
+<meta name="assert" content="padding supports the full grammar '<length-percentage>{1,4}'.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_valid_value("padding", "10px");
 test_valid_value("padding", "10px 20px 30px 40px");
 test_valid_value("padding", "calc(2em + 3ex)");
 
 test_valid_value("padding-top", "10px");
 test_valid_value("padding-right", "20px");
 test_valid_value("padding-bottom", "30px");
 test_valid_value("padding-left", "40px");
+
+test_valid_value("padding", "20%");
+test_valid_value("padding", "10px 20% 30% 40px");
+test_valid_value("padding-right", "20%");
+test_valid_value("padding-right", "calc(2em + 3%)");
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-flexbox/inline-flex-min-content-height.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Flexbox Layout Test: Atomic inline Flexible Box with height: min-content</title>
+<link rel="help" href="https://drafts.csswg.org/css-sizing/#valdef-width-min-content">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name=assert content="This test checks that height: min-content computes to 'auto' for atomic inline level flexible box.">
+<style>
+  #flexbox {
+    background-color: green;
+    display: inline-flex;
+    height: min-content;
+  }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="flexbox">
+  <div id="item">
+    <div style="width:100px; height:100px;"></div>
+  </div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-paint-api/custom-property-animation-on-main-thread.https.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
+<link rel="match" href="one-custom-property-animation-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  animation: expand 5s;
+}
+@keyframes expand {
+  0% { --foo: 0; }
+  0.01% { --foo: 50; }
+  99% { --foo: 50; }
+  100% { --foo: 100; }
+}
+
+#canvas-geometry {
+  background-color: blue;
+  background-image: paint(geometry);
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<body>
+<div id="canvas-geometry" class="container"></div>
+
+<script id="code" type="text/worklet">
+registerPaint('geometry', class {
+  static get inputProperties() { return ['--foo']; }
+  paint(ctx, geom, properties) {
+    let fooValue = parseFloat(properties.get('--foo').toString());
+    ctx.fillStyle = 'green';
+    ctx.fillRect(0, 0, fooValue, fooValue);
+  }
+});
+</script>
+
+<script>
+CSS.registerProperty({
+  name: '--foo',
+  syntax: '<number>',
+  initialValue: '0',
+  inherits: false
+});
+</script>
+
+<script>
+// The test is designed to make sure that when the custom property animation is
+// running on the compositor thread, we are getting the right value.
+// The "importWorkletAndTerminateTestAfterAsyncPaint" has the logic to rAF
+// two frames before taking a screenshot. So the animation is designed to
+// be stable after two frames. That is, the 0.01% of 5s is much less than
+// two frames, and thus after two frames, the value of --foo should be stable.
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-paint-api/one-custom-property-animation-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+<canvas id ="canvas" width="100" height="100"></canvas>
+<script>
+var canvas = document.getElementById('canvas');
+var context = canvas.getContext("2d");
+context.fillStyle = 'blue';
+context.fillRect(0, 0, 100, 100);
+context.fillStyle = 'green';
+context.fillRect(0, 0, 50, 50);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-paint-api/one-custom-property-animation.https.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
+<link rel="match" href="one-custom-property-animation-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  animation: expand 5s;
+  /* TODO(crbug.com/987969): the will-change: transform should not be needed. */
+  will-change: transform;
+}
+@keyframes expand {
+  0% { --foo: 0; }
+  0.01% { --foo: 50; }
+  99% { --foo: 50; }
+  100% { --foo: 100; }
+}
+
+#canvas-geometry {
+  background-color: blue;
+  background-image: paint(geometry);
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<body>
+<div id="canvas-geometry" class="container"></div>
+
+<script id="code" type="text/worklet">
+registerPaint('geometry', class {
+  static get inputProperties() { return ['--foo']; }
+  paint(ctx, geom, properties) {
+    let fooValue = parseFloat(properties.get('--foo').toString());
+    ctx.fillStyle = 'green';
+    ctx.fillRect(0, 0, fooValue, fooValue);
+  }
+});
+</script>
+
+<script>
+CSS.registerProperty({
+  name: '--foo',
+  syntax: '<number>',
+  initialValue: '0',
+  inherits: false
+});
+</script>
+
+<script>
+// The test is designed to make sure that when the custom property animation is
+// running on the compositor thread, we are getting the right value.
+// The "importWorkletAndTerminateTestAfterAsyncPaint" has the logic to rAF
+// two frames before taking a screenshot. So the animation is designed to
+// be stable after two frames. That is, the 0.01% of 5s is much less than
+// two frames, and thus after two frames, the value of --foo should be stable.
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-paint-api/two-custom-property-animation-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+<canvas id ="canvas" width="100" height="100"></canvas>
+<script>
+var canvas = document.getElementById('canvas');
+var context = canvas.getContext("2d");
+context.fillStyle = 'blue';
+context.fillRect(0, 0, 100, 100);
+context.fillStyle = '#9876c8';
+context.fillRect(0, 0, 50, 50);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-paint-api/two-custom-property-animation.https.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
+<link rel="match" href="two-custom-property-animation-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  animation: expand 5s, clr 5s;
+  /* TODO(crbug.com/987969): the will-change: transform should not be needed. */
+  will-change: transform;
+}
+@keyframes expand {
+  0% { --foo: 0; }
+  0.01% { --foo: 50; }
+  99% { --foo: 50; }
+  100% { --foo: 100; }
+}
+@keyframes clr {
+  0% { --bar: 0; }
+  0.01% { --bar: 200; }
+  99% { --bar: 200; }
+  100% { --bar: 255; }
+}
+
+#canvas-geometry {
+  background-color: blue;
+  background-image: paint(geometry);
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<body>
+<div id="canvas-geometry" class="container"></div>
+
+<script id="code" type="text/worklet">
+registerPaint('geometry', class {
+  static get inputProperties() { return ['--foo', '--bar']; }
+  paint(ctx, geom, properties) {
+    let fooValue = parseFloat(properties.get('--foo').toString());
+    let barValue = parseFloat(properties.get('--bar').toString());
+    let barString = barValue.toString(16);
+    if (barString.length == 1)
+      barString = '0' + barString;
+    ctx.fillStyle = '#9876' + barString;
+    ctx.fillRect(0, 0, fooValue, fooValue);
+  }
+});
+</script>
+
+<script>
+CSS.registerProperty({
+  name: '--foo',
+  syntax: '<number>',
+  initialValue: '0',
+  inherits: false
+});
+CSS.registerProperty({
+  name: '--bar',
+  syntax: '<number>',
+  initialValue: '0',
+  inherits: false
+});
+</script>
+
+<script>
+// The test is designed to make sure that when the custom property animation is
+// running on the compositor thread, we are getting the right value.
+// The "importWorkletAndTerminateTestAfterAsyncPaint" has the logic to rAF
+// two frames before taking a screenshot. So the animation is designed to
+// be stable after two frames. That is, the 0.01% of 5s is much less than
+// two frames, and thus after two frames, the value of --foo should be stable.
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/animations/bottom-interpolation.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#propdef-bottom">
+<meta name="test" content="bottom supports animation as a length or percentage">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  bottom: 30px;
+}
+.target {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  background-color: black;
+  display: inline-block;
+  margin: 20px 5px 20px 0px;
+  bottom: 10px;
+}
+.expected {
+  background-color: green;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'bottom',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.5, expect: '15px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_no_interpolation({
+  property: 'bottom',
+  from: 'initial',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'bottom',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.5, expect: '25px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_no_interpolation({
+  property: 'bottom',
+  from: 'unset',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'bottom',
+  from: '-10px',
+  to: '10px'
+}, [
+  {at: -0.3, expect: '-16px'},
+  {at: 0, expect: '-10px'},
+  {at: 0.5, expect: '0px'},
+  {at: 1, expect: '10px'},
+  {at: 1.5, expect: '20px'}
+]);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/animations/left-interpolation.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#propdef-left">
+<meta name="test" content="left supports animation as a length or percentage">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  position: relative;
+  left: 30px;
+}
+.target {
+  position: relative;
+  width: 10px;
+  height: 10px;
+  background-color: black;
+  left: 10px;
+}
+.expected {
+  background-color: green;
+}
+</style>
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'left',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.5, expect: '15px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_no_interpolation({
+  property: 'left',
+  from: 'initial',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'left',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.5, expect: '25px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_no_interpolation({
+  property: 'left',
+  from: 'unset',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'left',
+  from: '-10px',
+  to: '10px'
+}, [
+  {at: -0.3, expect: '-16px'},
+  {at: 0, expect: '-10px'},
+  {at: 0.5, expect: '0px'},
+  {at: 1, expect: '10px'},
+  {at: 1.5, expect: '20px'}
+]);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/animations/right-interpolation.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#propdef-right">
+<meta name="test" content="right supports animation as a length or percentage">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  margin-left: 40px;
+  right: 30px;
+}
+.target {
+  position: relative;
+  width: 10px;
+  height: 10px;
+  background-color: black;
+  right: 10px;
+}
+.expected {
+  background-color: green;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'right',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.5, expect: '15px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_no_interpolation({
+  property: 'right',
+  from: 'initial',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'right',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.5, expect: '25px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_no_interpolation({
+  property: 'right',
+  from: 'unset',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'right',
+  from: '-10px',
+  to: '10px'
+}, [
+  {at: -0.3, expect: '-16px'},
+  {at: 0, expect: '-10px'},
+  {at: 0.5, expect: '0px'},
+  {at: 1, expect: '10px'},
+  {at: 1.5, expect: '20px'}
+]);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-position/animations/top-interpolation.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#propdef-top">
+<meta name="assert" content="top supports animation as a length or percentage">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  top: 30px;
+  margin-bottom: 40px;
+}
+.target {
+  position: relative;
+  width: 10px;
+  height: 10px;
+  background-color: black;
+  display: inline-block;
+  top: 10px;
+}
+.expected {
+  background-color: green;
+  margin-right: 10px;
+}
+</style>
+
+<body></body>
+
+<script>
+test_interpolation({
+  property: 'top',
+  from: neutralKeyframe,
+  to: '20px',
+}, [
+  {at: -0.3, expect: '7px'},
+  {at: 0, expect: '10px'},
+  {at: 0.5, expect: '15px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '25px'},
+]);
+
+test_no_interpolation({
+  property: 'top',
+  from: 'initial',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'top',
+  from: 'inherit',
+  to: '20px',
+}, [
+  {at: -0.3, expect: '33px'},
+  {at: 0, expect: '30px'},
+  {at: 0.5, expect: '25px'},
+  {at: 1, expect: '20px'},
+  {at: 1.5, expect: '15px'},
+]);
+
+test_no_interpolation({
+  property: 'top',
+  from: 'unset',
+  to: '20px',
+});
+
+test_interpolation({
+  property: 'top',
+  from: '-10px',
+  to: '10px'
+}, [
+  {at: -0.3, expect: '-16px'},
+  {at: 0, expect: '-10px'},
+  {at: 0.5, expect: '0px'},
+  {at: 1, expect: '10px'},
+  {at: 1.5, expect: '20px'}
+]);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/first-letter-of-html-root-crash-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<body style="margin:0">PASS</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/first-letter-of-html-root-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>CSS Test: ::first-letter of html root element crash in combination with removal of body</title>
+<link rel="help" href="https://crbug.com/993764">
+<link rel="match" href="first-letter-of-html-root-crash-ref.html">
+<style id="sheet">
+  html::first-letter { font-size: initial }
+</style>FAIL
+<script>
+  const sel = window.getSelection();
+  sel.selectAllChildren(document.documentElement);
+  const range = sel.getRangeAt(0);
+  document.body.remove();
+  document.documentElement.appendChild(document.createTextNode("PASS"));
+  document.documentElement.offsetTop;
+  range.surroundContents(sheet);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-tables/table-cell-overflow-auto-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; border: solid;">
+  <div style="display: table-cell; max-width: 100px; height: 100px; background: green; overflow-x: scroll; vertical-align: top;">
+     <div style="width: 120px; height: 50px; background: hotpink;"></div>
+   </div>
+</div>
+<br>
+<div style="width: 100px; height: 100px; border: solid;">
+  <div style="display: table-cell; max-width: 100px; height: 100px; background: green; overflow-x: scroll; vertical-align: middle;">
+     <div style="width: 120px; height: 50px; background: hotpink;"></div>
+   </div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-tables/table-cell-overflow-auto.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#row-layout">
+<link rel="match" href="table-cell-overflow-auto-ref.html">
+<meta name="assert" content="horizontal scrollbars show up on table-cells with overflowing content and overflow:auto, vertical-align: middle/top" />
+<div style="width: 100px; height: 100px; border: solid;">
+  <div style="display: table-cell; max-width: 100px; height: 100px; background: green; overflow-x: auto; vertical-align: top;">
+     <div style="width: 120px; height: 50px; background: hotpink;"></div>
+   </div>
+</div>
+<br>
+<div style="width: 100px; height: 100px; border: solid;">
+  <div style="display: table-cell; max-width: 100px; height: 100px; background: green; overflow-x: auto; vertical-align: middle;">
+     <div style="width: 120px; height: 50px; background: hotpink;"></div>
+   </div>
+</div>
--- a/testing/web-platform/tests/css/css-transitions/CSSTransition-effect.tentative.html
+++ b/testing/web-platform/tests/css/css-transitions/CSSTransition-effect.tentative.html
@@ -106,19 +106,20 @@ promise_test(async t => {
 
   assert_equals(div.getAnimations().length, 1);
 
   const transition = div.getAnimations()[0];
   await transition.ready;
 
   // The transition needs to have a non-zero currentTime for the interruption
   // reversal logic to apply.
-  await singleFrame();
+  while (getComputedStyle(div).left == '0px') {
+    await singleFrame();
+  }
   assert_not_equals(transition.currentTime, 0);
-  assert_not_equals(getComputedStyle(div).left, '0px');
 
   // Without yielding to the rendering loop, set the current transition's
   // effect to null and interrupt the transition. This should work correctly.
   transition.effect = null;
   div.style.left = '0px';
 
   // Yield to the rendering loop. This should not crash.
   await singleFrame();
--- a/testing/web-platform/tests/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.html
+++ b/testing/web-platform/tests/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.html
@@ -22,9 +22,19 @@ test(() => {
 test(() => {
   assert_throws(new SyntaxError(), () => CSSNumericValue.parse('calc(calc(1px * 2s) + 3%)'));
 }, 'Parsing a calc with incompatible units throws a SyntaxError');
 
 test(() => {
   assert_style_value_equals(new CSSUnitValue(1, 'px'), CSSNumericValue.parse(' 1px  '));
 }, 'Parsing ignores surrounding spaces');
 
+test(() => {
+  const expected = new CSSMathMin(CSS.px(10), CSS.percent(10));
+  assert_style_value_equals(expected, CSSNumericValue.parse('min(10px, 10%)'));
+}, 'Parsing min() is successful');
+
+test(() => {
+  const expected = new CSSMathMax(CSS.px(10), CSS.percent(10));
+  assert_style_value_equals(expected, CSSNumericValue.parse('max(10px, 10%)'));
+}, 'Parsing max() is successful');
+
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-typed-om/width-by-max-px-em.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-typed-om-1/#stylevalue-subclasses">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="'width' can be set with a CSSMathMax object.">
+<style>
+#target { font-size: 10px; background-color: green; height: 100px; }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id=target></div>
+<script>
+const width = new CSSMathMax(CSS.em(10), CSS.px(90));
+const target = document.querySelector('#target');
+target.attributeStyleMap.set('width', width);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-typed-om/width-by-min-px-em.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-typed-om-1/#stylevalue-subclasses">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="'width' can be set with a CSSMathMin object.">
+<style>
+#target { font-size: 10px; background-color: green; height: 100px; }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id=target></div>
+<script>
+const width = new CSSMathMin(CSS.em(10), CSS.px(110));
+const target = document.querySelector('#target');
+target.attributeStyleMap.set('width', width);
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-values/max-length-percent-001.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS values: max() between pixel and percentage values</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="match" href="reference/200-200-green.html">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<style>
+html, body { margin: 0px; padding: 0px; }
+#parent { width: 400px; }
+#target {
+  width: max(100px, 25% + 100px, 150px + 10%);
+  height: 200px;
+  background: green;
+}
+</style>
+<div id=parent>
+  <div id=target></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-values/min-length-percent-001.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS values: min() between pixel and percentage values</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="match" href="reference/200-200-green.html">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<style>
+html, body { margin: 0px; padding: 0px; }
+#parent { width: 400px; }
+#target {
+  width: min(300px, 25% + 100px, 50px + 50%);
+  height: 200px;
+  background: green;
+}
+</style>
+<div id=parent>
+  <div id=target></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/element-timing/toJSON.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Element Timing: toJSON</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/element-timing-helpers.js"></script>
+<img elementtiming='img' src="resources/square100.png"/>
+<script>
+  async_test(function (t) {
+    if (!window.PerformanceElementTiming) {
+      assert_unreached("PerformanceElementTiming is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        assert_equals(entryList.getEntries().length, 1);
+        const entry = entryList.getEntries()[0];
+        assert_equals(typeof(entry.toJSON), 'function');
+        const json = entry.toJSON();
+        assert_equals(typeof(json), 'object');
+        const keys = [
+          // PerformanceEntry
+          'name',
+          'entryType',
+          'startTime',
+          'duration',
+          // PerformanceElementTiming
+          'renderTime',
+          'loadTime',
+          'intersectionRect',
+          'identifier',
+          'naturalWidth',
+          'naturalHeight',
+          'id',
+          'element',
+          'url',
+        ];
+        for (const key of keys) {
+          assert_equals(json[key], entry[key],
+            'PerformanceElementTiming ${key} entry does not match its toJSON value');
+        }
+      })
+    );
+    observer.observe({type: 'element', buffered: true});
+  }, 'Test toJSON() in PerformanceElementTiming.');
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/event-timing/toJSON.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Event Timing: toJSON</title>
+<body>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=resources/event-timing-test-utils.js></script>
+<button id='button'>Generate a 'click' event</button>
+<script>
+  async_test(function (t) {
+    if (!window.PerformanceEventTiming) {
+      assert_unreached("PerformanceEventTiming is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        const entry = entryList.getEntries()[0];
+        assert_equals(typeof(entry.toJSON), 'function');
+        const json = entry.toJSON();
+        assert_equals(typeof(json), 'object');
+        const keys = [
+          // PerformanceEntry
+          'name',
+          'entryType',
+          'startTime',
+          'duration',
+          // PerformanceEventTiming
+          'processingStart',
+          'processingEnd',
+          'cancelable',
+        ];
+        for (const key of keys) {
+          assert_equals(json[key], entry[key],
+            'PerformanceEventTiming ${key} entry does not match its toJSON value');
+        }
+      })
+    );
+    observer.observe({type: 'event'});
+    clickAndBlockMain('button');
+  }, 'Test toJSON() in PerformanceEventTiming.');
+</script>
+</body>
--- a/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/coep-navigate-popup.https.html
@@ -1,10 +1,11 @@
 <!doctype html>
 <title>Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: a navigating popup</title>
+<meta content=timeout value=long>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src="/common/get-host-info.sub.js"></script>
 <script src="resources/common.js"></script>
 <script>
 [
   {
     "title": "coop/coep",
--- a/testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js
+++ b/testing/web-platform/tests/html/cross-origin-opener-policy/resources/common.js
@@ -24,13 +24,13 @@ function coop_coep_test(t, host, coop, c
 function coop_test(t, host, coop, channelName, hasOpener) {
   coop_coep_test(t, host, coop, "", channelName, hasOpener);
 }
 
 function run_coop_tests(documentCOOPValueTitle, testArray) {
   for (const test of tests) {
     async_test(t => {
       coop_test(t, test[0], test[1],
-                `${mainTest}_to_${test[0].name}_${test[1].replace(/ /g,"-")}`,
+                `${documentCOOPValueTitle}_to_${test[0].name}_${test[1].replace(/ /g,"-")}`,
                 test[2]);
     }, `${documentCOOPValueTitle} document opening popup to ${test[0].origin} with COOP: "${test[1]}"`);
   }
 }
--- a/testing/web-platform/tests/html/dom/interfaces.https.html
+++ b/testing/web-platform/tests/html/dom/interfaces.https.html
@@ -218,15 +218,16 @@ idl_test(
       MessageEvent: ['new MessageEvent("message", { data: 5 })'],
       MessageChannel: [],
       MessagePort: [],
       HTMLMarqueeElement: ['document.createElement("marquee")'],
       HTMLFrameSetElement: ['document.createElement("frameset")'],
       HTMLFrameElement: ['document.createElement("frame")'],
       HTMLDirectoryElement: ['document.createElement("dir")'],
       HTMLFontElement: ['document.createElement("font")'],
+      DOMStringList: ['location.ancestorOrigins'],
     });
     idlArray.prevent_multiple_testing('HTMLElement');
     await waitForLoad;
   }
 );
 
 </script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>DOMStringList IDL tests</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<script src=/resources/WebIDLParser.js></script>
-<script src=/resources/idlharness.js></script>
-
-<h1>DOMStringList IDL tests</h1>
-<div id=log></div>
-
-<script>
-"use strict";
-async_test(function(t) {
-  var request = new XMLHttpRequest();
-  request.open("GET", "/interfaces/html.idl");
-  request.send();
-  request.onload = t.step_func(function() {
-    var idlArray = new IdlArray();
-    var idls = request.responseText;
-
-    idlArray.add_idls(idls, { only: ["DOMStringList"] });
-
-    idlArray.add_objects({
-      DOMStringList: ['location.ancestorOrigins'],
-    });
-
-    idlArray.test();
-    t.done();
-  });
-});
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/infrastructure/common-dom-interfaces/collections/domstringlist-interface.worker.js
+++ /dev/null
@@ -1,24 +0,0 @@
-"use strict";
-
-importScripts("/resources/testharness.js");
-importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js");
-
-async_test(function(t) {
-  var request = new XMLHttpRequest();
-  request.open("GET", "/interfaces/html.idl");
-  request.send();
-  request.onload = t.step_func(function() {
-    var idlArray = new IdlArray();
-    var idls = request.responseText;
-
-    idlArray.add_idls(idls, { only: ["DOMStringList"] });
-
-    idlArray.add_objects({
-      DOMStringList: [],
-    });
-    idlArray.test();
-    t.done();
-  });
-});
-
-done();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: tabIndex getter return value</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<input>
+<input type="hidden">
+<button>button</button>
+<button disabled>button</button>
+<button hidden>button</button>
+<a>a</a>
+<a href="#">a</a>
+<svg><a>svg a</a></svg>
+<svg><a>svg a</a></svg>
+<link id="nohref">
+<textarea></textarea>
+<select><optgroup><option>option</option></optgroup></select>
+<select multiple></select>
+<iframe width="10" height="10"></iframe>
+<embed width="10" height="10"></embed>
+<object width="10" height="10"></object>
+<span></span>
+<div></div>
+<details><summary>summary</summary><summary id="secondsummary">second summary</summary>details</details>
+<div id="hostDelegatesFocus"></div>
+<div id="hostNonDelegatesFocus"></div>
+<script>
+document.getElementById("hostDelegatesFocus").attachShadow({ mode: "open", delegatesFocus: true });
+document.getElementById("hostNonDelegatesFocus").attachShadow({ mode: "open", delegatesFocus: false });
+const defaultList = [
+  ["input", 0],
+  ["input[type='hidden']", 0],
+  ["button", 0],
+  ["button[disabled]", 0],
+  ["button[hidden]", 0],
+  ["a", 0],
+  ["a[href]", 0],
+  ["svg a", 0],
+  ["textarea", 0],
+  ["select", 0],
+  ["select[multiple]", 0],
+  ["option", -1],
+  ["optgroup", -1],
+  ["iframe", 0],
+  ["embed", -1],
+  ["object", 0],
+  ["span", -1],
+  ["div", -1],
+  ["link#nohref", -1],
+  ["link[href]", -1],
+  ["details", -1],
+  ["summary", 0],
+  ["summary#secondsummary", -1],
+  ["#hostDelegatesFocus", -1],
+  ["#hostNonDelegatesFocus", -1],
+];
+const tabIndexValue = [-1, 0, 1];
+for (const entry of defaultList) {
+  const element = document.querySelector(entry[0]);
+  test(() => {
+    assert_equals(element.tabIndex, entry[1]);
+  }, entry[0] + ".tabIndex should return " + entry[1] + " by default");
+  for (const setValue of tabIndexValue ) {
+    test(() => {
+      element.setAttribute("tabindex", setValue);
+      assert_equals(element.tabIndex, setValue);
+    }, entry[0] + ".tabIndex should return " + setValue + " when set to " + setValue);
+  }
+}
+</script>
+</body>
+
deleted file mode 100644
--- a/testing/web-platform/tests/html/webappapis/animation-frames/idlharness.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8" />
-<title>idlharness test</title>
-<link rel="author" title="Kensaku Komatsu" href="mailto:kensaku.komatsu@gmail.com" />
-<link rel="help" href="http://www.w3.org/TR/animation-timing/#definitions"/>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/WebIDLParser.js"></script>
-<script src="/resources/idlharness.js"></script>
-</head>
-<body>
-<h1>idlharness test</h1>
-<p>This test validates the WebIDL included in the Timing control for script-based animations specification.</p>
-
-<pre id='untested_idl' style='display:none'>
-[Global=Window, Exposed=Window]
-interface Window {
-};
-</pre>
-
-<pre id='idl'>
-partial interface Window {
-  long requestAnimationFrame(FrameRequestCallback callback);
-  void cancelAnimationFrame(long handle);
-};
-
-callback FrameRequestCallback = void (DOMHighResTimeStamp time);
-</pre>
-
-<script>
-
-(function() {
-  var idl_array = new IdlArray();
-
-  idl_array.add_untested_idls(document.getElementById("untested_idl").textContent);
-  idl_array.add_idls(document.getElementById("idl").textContent);
-
-  idl_array.add_objects({Window: ["window"]});
-
-  idl_array.test();
-})();
-
-</script>
-
-<div id="log"></div>
-
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/infrastructure/assumptions/document-fonts-ready.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<title>document.fonts.ready resolves after layout depending on loaded fonts</title>
+<link rel="help" href="https://drafts.csswg.org/css-font-loading/#fontfaceset-pending-on-the-environment">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+  #foo {
+      font: 100px/1 Ahem;
+  }
+</style>
+<div id="log"></div>
+<span id="foo">X</span>
+<script>
+  // The purpose of this test is to ensure that testharness.js tests can use
+  // `document.fonts.ready` to wait for a web font to load, without having to
+  // wait for the window load event before or requestAnimationFrame after.
+  //
+  // The spec says that a FontFaceSet is "pending on the environment" if "the
+  // document has pending layout operations which might cause the user agent to
+  // request a font, or which depend on recently-loaded fonts", and both are
+  // assumed to hold true in this test.
+  async_test(t => {
+    assert_equals(document.fonts.size, 1, 'one font is pending');
+    document.fonts.ready.then(t.step_func_done(() => {
+      const span = document.getElementById('foo');
+      const rect = span.getBoundingClientRect();
+      // If Ahem has loaded, the X will be 100px wide.
+      assert_equals(rect.width, 100, 'span is 100px wide');
+    }));
+  });
+</script>
--- a/testing/web-platform/tests/interfaces/element-timing.idl
+++ b/testing/web-platform/tests/interfaces/element-timing.idl
@@ -8,13 +8,14 @@ interface PerformanceElementTiming : Per
     readonly attribute DOMHighResTimeStamp loadTime;
     readonly attribute DOMRectReadOnly intersectionRect;
     readonly attribute DOMString identifier;
     readonly attribute unsigned long naturalWidth;
     readonly attribute unsigned long naturalHeight;
     readonly attribute DOMString id;
     readonly attribute Element? element;
     readonly attribute DOMString url;
+    [Default] object toJSON();
 };
 
 partial interface Element {
     [CEReactions] attribute DOMString elementTiming;
 };
--- a/testing/web-platform/tests/interfaces/event-timing.idl
+++ b/testing/web-platform/tests/interfaces/event-timing.idl
@@ -3,16 +3,17 @@
 // (https://github.com/tidoust/reffy-reports)
 // Source: Event Timing API (https://wicg.github.io/event-timing/)
 
 [Exposed=Window]
 interface PerformanceEventTiming : PerformanceEntry {
     readonly attribute DOMHighResTimeStamp processingStart;
     readonly attribute DOMHighResTimeStamp processingEnd;
     readonly attribute boolean cancelable;
+    [Default] object toJSON();
 };
 
 [Exposed=Window]
 interface EventCounts {
     readonly maplike<DOMString, unsigned long long>;
 };
 
 [Exposed=Window]
--- a/testing/web-platform/tests/interfaces/largest-contentful-paint.idl
+++ b/testing/web-platform/tests/interfaces/largest-contentful-paint.idl
@@ -5,9 +5,10 @@
 
 interface LargestContentfulPaint : PerformanceEntry {
     readonly attribute DOMHighResTimeStamp renderTime;
     readonly attribute DOMHighResTimeStamp loadTime;
     readonly attribute unsigned long size;
     readonly attribute DOMString id;
     readonly attribute DOMString url;
     readonly attribute Element? element;
+    [Default] object toJSON();
 };
--- a/testing/web-platform/tests/interfaces/layout-instability.idl
+++ b/testing/web-platform/tests/interfaces/layout-instability.idl
@@ -2,9 +2,10 @@
 // Content was automatically extracted by Reffy into reffy-reports
 // (https://github.com/tidoust/reffy-reports)
 // Source: Layout Instability API (https://wicg.github.io/layout-instability/)
 
 interface LayoutShift : PerformanceEntry {
   readonly attribute long value;
   readonly attribute boolean hadRecentInput;
   readonly attribute DOMHighResTimeStamp lastInputTime;
+  [Default] object toJSON();
 };
--- a/testing/web-platform/tests/interfaces/webrtc-stats.idl
+++ b/testing/web-platform/tests/interfaces/webrtc-stats.idl
@@ -65,30 +65,50 @@ dictionary RTCReceivedRtpStreamStats : R
 };
 
 dictionary RTCInboundRtpStreamStats : RTCReceivedRtpStreamStats {
  DOMString trackId;
  DOMString receiverId;
  DOMString remoteId;
  unsigned long framesDecoded;
  unsigned long keyFramesDecoded;
+ unsigned long frameWidth;
+ unsigned long frameHeight;
+ double framesPerSecond;
  unsigned long long qpSum;
  double totalDecodeTime;
+ boolean voiceActivityFlag;
  DOMHighResTimeStamp lastPacketReceivedTimestamp;
  double averageRtcpInterval;
  unsigned long fecPacketsReceived;
  unsigned long fecPacketsDiscarded;
  unsigned long long bytesReceived;
  unsigned long packetsFailedDecryption;
  unsigned long packetsDuplicated;
  record<USVString, unsigned long> perDscpPacketsReceived;
  unsigned long nackCount;
  unsigned long firCount;
  unsigned long pliCount;
  unsigned long sliCount;
+ DOMHighResTimeStamp estimatedPlayoutTimestamp;
+ double jitterBufferDelay;
+ unsigned long long jitterBufferEmittedCount;
+ unsigned long long totalSamplesReceived;
+ unsigned long long concealedSamples;
+ unsigned long long silentConcealedSamples;
+ unsigned long long concealmentEvents;
+ unsigned long long insertedSamplesForDeceleration;
+ unsigned long long removedSamplesForAcceleration;
+ double audioLevel;
+ double totalAudioEnergy;
+ double totalSamplesDuration;
+ unsigned long framesReceived;
+ unsigned long framesDropped;
+ unsigned long partialFramesLost;
+ unsigned long fullFramesLost;
 };
 
 dictionary RTCRemoteInboundRtpStreamStats : RTCReceivedRtpStreamStats {
              DOMString localId;
              double roundTripTime;
              double fractionLost;
 };
 
@@ -105,19 +125,26 @@ dictionary RTCOutboundRtpStreamStats : R
              DOMString mediaSourceId;
              DOMString senderId;
              DOMString remoteId;
              DOMHighResTimeStamp lastPacketSentTimestamp;
              unsigned long long retransmittedPacketsSent;
              unsigned long long retransmittedBytesSent;
              double targetBitrate;
              unsigned long long totalEncodedBytesTarget;
+             unsigned long frameWidth;
+             unsigned long frameHeight;
+             double framesPerSecond;
+             unsigned long framesSent;
+             unsigned long hugeFramesSent;
              unsigned long framesEncoded;
              unsigned long keyFramesEncoded;
              unsigned long long qpSum;
+             unsigned long long totalSamplesSent;
+             boolean voiceActivityFlag;
              double totalEncodeTime;
              double totalPacketSendDelay;
              double averageRtcpInterval;
              RTCQualityLimitationReason qualityLimitationReason;
              record<DOMString, double> qualityLimitationDurations;
              record<USVString, unsigned long> perDscpPacketsSent;
              unsigned long nackCount;
              unsigned long firCount;
@@ -141,16 +168,18 @@ dictionary RTCMediaSourceStats : RTCStat
              DOMString trackIdentifier;
              DOMString kind;
 };
 
 dictionary RTCAudioSourceStats : RTCMediaSourceStats {
               double audioLevel;
               double totalAudioEnergy;
               double totalSamplesDuration;
+              double echoReturnLoss;
+              double echoReturnLossEnhancement;
 };
 
 dictionary RTCVideoSourceStats : RTCMediaSourceStats {
              unsigned long width;
              unsigned long height;
              unsigned long frames;
              unsigned long framesPerSecond;
 };
@@ -178,69 +207,39 @@ dictionary RTCMediaHandlerStats : RTCSta
              DOMString trackIdentifier;
              boolean remoteSource;
              boolean ended;
              DOMString kind;
              RTCPriorityType priority;
 };
 
 dictionary RTCVideoHandlerStats : RTCMediaHandlerStats {
-             unsigned long frameWidth;
-             unsigned long frameHeight;
-             double framesPerSecond;
 };
 
 dictionary RTCVideoSenderStats : RTCVideoHandlerStats {
              DOMString mediaSourceId;
-             unsigned long framesCaptured;
-             unsigned long framesSent;
-             unsigned long hugeFramesSent;
 };
 
 dictionary RTCSenderVideoTrackAttachmentStats : RTCVideoSenderStats {
 };
 
 dictionary RTCVideoReceiverStats : RTCVideoHandlerStats {
-             DOMHighResTimeStamp estimatedPlayoutTimestamp;
-             double jitterBufferDelay;
-             unsigned long long jitterBufferEmittedCount;
-             unsigned long framesReceived;
-             unsigned long framesDecoded;
-             unsigned long framesDropped;
-             unsigned long partialFramesLost;
-             unsigned long fullFramesLost;
 };
 
 dictionary RTCAudioHandlerStats : RTCMediaHandlerStats {
-             boolean voiceActivityFlag;
 };
 
 dictionary RTCAudioSenderStats : RTCAudioHandlerStats {
              DOMString mediaSourceId;
-             double echoReturnLoss;
-             double echoReturnLossEnhancement;
-             unsigned long long totalSamplesSent;
 };
 
 dictionary RTCSenderAudioTrackAttachmentStats : RTCAudioSenderStats {
 };
 
 dictionary RTCAudioReceiverStats : RTCAudioHandlerStats {
-             DOMHighResTimeStamp estimatedPlayoutTimestamp;
-             double jitterBufferDelay;
-             unsigned long long jitterBufferEmittedCount;
-             unsigned long long totalSamplesReceived;
-             unsigned long long concealedSamples;
-             unsigned long long silentConcealedSamples;
-             unsigned long long concealmentEvents;
-             unsigned long long insertedSamplesForDeceleration;
-             unsigned long long removedSamplesForAcceleration;
-             double audioLevel;
-             double totalAudioEnergy;
-             double totalSamplesDuration;
 };
 
 dictionary RTCDataChannelStats : RTCStats {
              DOMString label;
              DOMString protocol;
              long dataChannelIdentifier;
              DOMString transportId;
              RTCDataChannelState state;
@@ -362,17 +361,56 @@ partial dictionary RTCRtpStreamStats {
 partial dictionary RTCInboundRtpStreamStats {
           double fractionLost;
 };
 
 partial dictionary RTCAudioHandlerStats {
             double audioLevel;
             double totalAudioEnergy;
             double totalSamplesDuration;
+            boolean voiceActivityFlag;
+};
+
+partial dictionary RTCAudioSenderStats {
+            unsigned long long totalSamplesSent;
+            double echoReturnLoss;
+            double echoReturnLossEnhancement;
+};
+
+partial dictionary RTCAudioReceiverStats {
+            DOMHighResTimeStamp estimatedPlayoutTimestamp;
+            double jitterBufferDelay;
+            unsigned long long jitterBufferEmittedCount;
+            unsigned long long totalSamplesReceived;
+            unsigned long long concealedSamples;
+            unsigned long long silentConcealedSamples;
+            unsigned long long concealmentEvents;
+            unsigned long long insertedSamplesForDeceleration;
+            unsigned long long removedSamplesForAcceleration;
+            double audioLevel;
+            double totalAudioEnergy;
+            double totalSamplesDuration;
+};
+
+partial dictionary RTCVideoHandlerStats {
+          unsigned long frameWidth;
+          unsigned long frameHeight;
+          double framesPerSecond;
 };
 
 partial dictionary RTCVideoSenderStats {
           unsigned long keyFramesSent;
+          unsigned long framesCaptured;
+          unsigned long framesSent;
+          unsigned long hugeFramesSent;
 };
 
 partial dictionary RTCVideoReceiverStats {
           unsigned long keyFramesReceived;
+          DOMHighResTimeStamp estimatedPlayoutTimestamp;
+          double jitterBufferDelay;
+          unsigned long long jitterBufferEmittedCount;
+          unsigned long framesReceived;
+          unsigned long framesDecoded;
+          unsigned long framesDropped;
+          unsigned long partialFramesLost;
+          unsigned long fullFramesLost;
 };
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/largest-contentful-paint/first-letter-background.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: observe element with background image in its first letter</title>
+<body>
+<style>
+#target::first-letter {
+  background-image: url('/images/black-rectangle.png');
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/element-timing-helpers.js"></script>
+<script>
+  let beforeRender = performance.now();
+  async_test(function (t) {
+    if (!window.LargestContentfulPaint) {
+      assert_unreached("LargestContentfulPaint is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func(function(entryList) {
+        entryList.getEntries().forEach(entry => {
+          // If we happen to get a text entry due to text happening before the image, return.
+          if (entry.url === '')
+            return;
+          assert_equals(entry.entryType, 'largest-contentful-paint');
+          assert_greater_than_equal(entry.renderTime, beforeRender,
+            'The rendering timestamp should occur after script starts running.');
+          assert_greater_than_equal(performance.now(), entry.renderTime,
+            'The rendering timestamp should occur before the entry is dispatched to the observer.');
+          assert_equals(entry.startTime, entry.renderTime, 'startTime should equal renderTime');
+          assert_equals(entry.duration, 0);
+          assert_greater_than_equal(entry.size, 0);
+          assert_equals(entry.id, 'target');
+          const pathname = window.location.origin + '/images/black-rectangle.png';
+          assert_equals(entry.url, pathname);
+          assert_greater_than(entry.loadTime, beforeRender,
+            'The load timestamp should occur after script starts running.');
+          assert_less_than(entry.loadTime, entry.renderTime,
+            'The load timestamp should occur before the render timestamp.')
+          assert_equals(entry.element, document.getElementById('target'));
+          t.done();
+      })
+    }));
+    observer.observe({entryTypes: ['largest-contentful-paint']});
+  }, 'Largest Contentful Paint: first-letter is observable.');
+</script>
+<div id='target'>A</div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/largest-contentful-paint/toJSON.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: toJSON</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>Text!</p>
+<script>
+  async_test(function (t) {
+    if (!window.LargestContentfulPaint) {
+      assert_unreached("LargestContentfulPaint is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        const entry = entryList.getEntries()[0];
+        assert_equals(typeof(entry.toJSON), 'function');
+        const json = entry.toJSON();
+        assert_equals(typeof(json), 'object');
+        const keys = [
+          // PerformanceEntry
+          'name',
+          'entryType',
+          'startTime',
+          'duration',
+          // LargestContentfulPaint
+          'renderTime',
+          'loadTime',
+          'size',
+          'id',
+          'url',
+          'element',
+        ];
+        for (const key of keys) {
+          assert_equals(json[key], entry[key],
+            'LargestContentfulPaint ${key} entry does not match its toJSON value');
+        }
+      })
+    );
+    observer.observe({type: 'largest-contentful-paint', buffered: true});
+  }, 'Test toJSON() in LargestContentfulPaint.');
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/layout-instability/toJSON.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Layout Instability: toJSON</title>
+<body>
+<style>
+#myDiv { position: relative; width: 300px; height: 100px; }
+</style>
+<div id='myDiv'></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  async_test(function (t) {
+    if (!window.LayoutShift)
+      assert_unreached('LayoutShift entries are not supported');
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        const entry = entryList.getEntries()[0];
+        assert_equals(typeof(entry.toJSON), 'function');
+        const json = entry.toJSON();
+        assert_equals(typeof(json), 'object');
+        const keys = [
+          // PerformanceEntry
+          'name',
+          'entryType',
+          'startTime',
+          'duration',
+          // LayoutShift
+          'value',
+          'hadRecentInput',
+          'lastInputTime',
+        ];
+        for (const key of keys) {
+          assert_equals(json[key], entry[key],
+            'LayoutShift ${key} entry does not match its toJSON value');
+        }
+      })
+    );
+    observer.observe({type: 'layout-shift'});
+    document.getElementById('myDiv').style = "top: 60px";
+  }, 'Test toJSON() in LayoutShift.');
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/inheritance.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Inheritance of touch-action</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action does not inherit.">
+<meta name="assert" content="touch-action initial value is auto.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+assert_not_inherited('touch-action', 'auto', 'none');
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-computed.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Pointer Events: getComputedStyle().touchAction</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("touch-action", "auto");
+test_computed_value("touch-action", "none");
+test_computed_value("touch-action", "manipulation");
+
+test_computed_value("touch-action", "pan-x");
+test_computed_value("touch-action", "pan-y");
+test_computed_value("touch-action", "pan-x pan-y");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-invalid.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Pointer Events: parsing touch-action with invalid values</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action supports only the grammar 'auto | none | [ pan-x || pan-y ] | manipulation'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("touch-action", "auto none");
+test_invalid_value("touch-action", "manipulation pan-x");
+test_invalid_value("touch-action", "pan-y pan-x pan-y");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/pointerevents/parsing/touch-action-valid.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Pointer Events: parsing touch-action with valid values</title>
+<link rel="help" href="https://w3c.github.io/pointerevents/#the-touch-action-css-property">
+<meta name="assert" content="touch-action supports the full grammar 'auto | none | [ pan-x || pan-y ] | manipulation'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("touch-action", "auto");
+test_valid_value("touch-action", "none");
+test_valid_value("touch-action", "manipulation");
+
+// [ pan-x || pan-y ]
+test_valid_value("touch-action", "pan-x");
+test_valid_value("touch-action", "pan-y");
+test_valid_value("touch-action", "pan-y pan-x", "pan-x pan-y");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/resources/chromium/nfc-mock.js
@@ -0,0 +1,415 @@
+'use strict';
+
+function toMojoNDEFRecordType(type) {
+  switch (type) {
+  case 'text':
+    return device.mojom.NDEFRecordType.TEXT;
+  case 'url':
+    return device.mojom.NDEFRecordType.URL;
+  case 'json':
+    return device.mojom.NDEFRecordType.JSON;
+  case 'opaque':
+    return device.mojom.NDEFRecordType.OPAQUE_RECORD;
+  }
+
+  return device.mojom.NDEFRecordType.EMPTY;
+}
+
+function toMojoNFCPushTarget(target) {
+  switch (target) {
+  case 'peer':
+    return device.mojom.NFCPushTarget.PEER;
+  case 'tag':
+    return device.mojom.NFCPushTarget.TAG;
+  }
+
+  return device.mojom.NFCPushTarget.ANY;
+}
+
+function toMojoNDEFCompatibility(compatibility) {
+  if (compatibility === 'nfc-forum')
+    return device.mojom.NDEFCompatibility.NFC_FORUM;
+  if (compatibility === 'vendor')
+    return device.mojom.NDEFCompatibility.VENDOR;
+  return device.mojom.NDEFCompatibility.ANY;
+}
+
+// Converts between NDEFMessageInit https://w3c.github.io/web-nfc/#dom-ndefmessage
+// and mojom.NDEFMessage structure, so that watch function can be tested.
+function toMojoNDEFMessage(message) {
+  let ndefMessage = new device.mojom.NDEFMessage();
+  ndefMessage.url = message.url;
+  ndefMessage.data = [];
+  for (let record of message.records) {
+    ndefMessage.data.push(toMojoNDEFRecord(record));
+  }
+  return ndefMessage;
+}
+
+function toMojoNDEFRecord(record) {
+  let nfcRecord = new device.mojom.NDEFRecord();
+  nfcRecord.recordType = toMojoNDEFRecordType(record.recordType);
+  nfcRecord.mediaType = record.mediaType;
+  nfcRecord.data = toByteArray(record.data);
+  return nfcRecord;
+}
+
+function toByteArray(data) {
+  // Convert JS objects to byte array
+  let byteArray = new Uint8Array(0);
+  let tmpData = data;
+
+  if (tmpData instanceof ArrayBuffer)
+    byteArray = new Uint8Array(tmpData);
+  else if (typeof tmpData === 'object' || typeof tmpData === 'number')
+    tmpData = JSON.stringify(tmpData);
+
+  if (typeof tmpData === 'string')
+    byteArray = new TextEncoder('utf-8').encode(tmpData);
+
+  return byteArray;
+}
+
+// Compares NDEFRecords that were provided / received by the mock service.
+// TODO: Use different getters to get received record data,
+// see spec changes at https://github.com/w3c/web-nfc/pull/243
+function compareNDEFRecords(providedRecord, receivedRecord) {
+  assert_equals(toMojoNDEFRecordType(providedRecord.recordType),
+                receivedRecord.recordType);
+
+  // Compare media types without charset.
+  // Charset should be compared when watch method is implemented, in order
+  // to check that written and read strings are equal.
+  assert_equals(providedRecord.mediaType,
+      receivedRecord.mediaType.substring(0, providedRecord.mediaType.length));
+
+  assert_false(toMojoNDEFRecordType(providedRecord.recordType) ==
+              device.mojom.NDEFRecordType.EMPTY);
+
+  assert_array_equals(toByteArray(providedRecord.data),
+                      new Uint8Array(receivedRecord.data));
+}
+
+// Compares NFCPushOptions structures that were provided to API and
+// received by the mock mojo service.
+function assertNFCPushOptionsEqual(provided, received) {
+  if (provided.ignoreRead !== undefined)
+    assert_equals(provided.ignoreRead, !!received.ignoreRead);
+  else
+    assert_equals(!!received.ignore_read, true);
+
+  if (provided.timeout !== undefined)
+    assert_equals(provided.timeout, received.timeout);
+  else
+    assert_equals(received.timeout, Infinity);
+
+  if (provided.target !== undefined)
+    assert_equals(toMojoNFCPushTarget(provided.target), received.target);
+  else
+    assert_equals(received.target, device.mojom.NFCPushTarget.ANY);
+
+  if (provided.compatibility !== undefined) {
+    assert_equals(toMojoNDEFCompatibility(provided.compatibility),
+        received.compatibility);
+  } else {
+    assert_equals(received.compatibility,
+        device.mojom.NDEFCompatibility.NFC_FORUM);
+  }
+}
+
+// Compares NFCReaderOptions structures that were provided to API and
+// received by the mock mojo service.
+function assertNFCReaderOptionsEqual(provided, received) {
+  if (provided.url !== undefined)
+    assert_equals(provided.url, received.url);
+  else
+    assert_equals(received.url, '');
+
+  if (provided.mediaType !== undefined)
+    assert_equals(provided.mediaType, received.mediaType);
+  else
+    assert_equals(received.mediaType, '');
+
+  if (provided.compatibility !== undefined) {
+    assert_equals(toMojoNDEFCompatibility(provided.compatibility),
+        received.compatibility);
+  } else {
+    assert_equals(received.compatibility,
+        device.mojom.NDEFCompatibility.NFC_FORUM);
+  }
+
+  if (provided.recordType !== undefined) {
+    assert_equals(!+received.record_filter, true);
+    assert_equals(toMojoNDEFRecordType(provided.recordType),
+        received.recordFilter.recordType);
+  }
+}
+
+// Checks whether NFCReaderOptions are matched with given message
+// and mock nfc's compatibility
+function matchesWatchOptions(message, compatibility, options) {
+  // Filter by NDEFCompatibility
+  if (options.compatibility !== toMojoNDEFCompatibility("any")
+      && options.compatibility !== compatibility) {
+    return false;
+  }
+
+  // Filter by Web NFC id
+  if (!matchesWebNfcId(message.url, options.url)) return false;
+
+  // Matches any record / media type.
+  if ((options.mediaType == null || options.mediaType === "")
+      && options.recordFilter == null) {
+    return true;
+  }
+
+  // Filter by mediaType and recordType
+  for (let record of message.records) {
+    if (options.mediaType != null && options.mediaType !== ""
+        && options.mediaType !== record.mediaType) {
+      return false;
+    }
+    if (options.recordFilter != null
+        && options.recordFilter.recordType
+            !== toMojoNDEFRecordType(record.recordType)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// Web NFC id match algorithm.
+// https://w3c.github.io/web-nfc/#url-pattern-match-algorithm
+function matchesWebNfcId(id, pattern) {
+  if (id != null && id !== "" && pattern != null && pattern !== "") {
+    const id_url = new URL(id);
+    const pattern_url = new URL(pattern);
+
+    if (id_url.protocol !== pattern_url.protocol) return false;
+    if (!id_url.host.endsWith("." + pattern_url.host)
+        && id_url.host !== pattern_url.host) {
+      return false;
+    }
+    if (pattern_url.pathname === "/*") return true;
+    if (id_url.pathname.startsWith(pattern_url.pathname)) return true;
+
+    return false;
+  }
+
+  return true;
+}
+
+function createNFCError(type) {
+  return { error: type ?
+      new device.mojom.NFCError({ errorType: type }) : null };
+}
+
+var WebNFCTest = (() => {
+  class MockNFC {
+    constructor() {
+      this.bindingSet_ = new mojo.BindingSet(device.mojom.NFC);
+
+      this.interceptor_ = new MojoInterfaceInterceptor(
+          device.mojom.NFC.name);
+      this.interceptor_.oninterfacerequest =
+          e => this.bindingSet_.addBinding(this, e.handle);
+      this.interceptor_.start();
+
+      this.hw_status_ = NFCHWStatus.ENABLED;
+      this.pushed_message_ = null;
+      this.push_options_ = null;
+      this.pending_promise_func_ = null;
+      this.push_completed_ = true;
+      this.push_should_timeout_ = false;
+      this.client_ = null;
+      this.watchers_ = [];
+      this.reading_messages_ = [];
+    }
+
+    // NFC delegate functions
+    async push(message, options) {
+      let error = this.getHWError();
+      if (error)
+        return error;
+
+      this.pushed_message_ = message;
+      this.push_options_ = options;
+
+      return new Promise(resolve => {
+        this.pending_promise_func_ = resolve;
+        if (options.timeout && options.timeout !== Infinity &&
+            !this.push_completed_) {
+          // Resolve with TimeoutError, else pending push operation.
+          if (this.push_should_timeout_) {
+            resolve(
+                createNFCError(device.mojom.NFCErrorType.TIMER_EXPIRED));
+          }
+        } else {
+          resolve(createNFCError(null));
+        }
+      });
+    }
+
+    async cancelPush(target) {
+      if (this.push_options_ && ((target === device.mojom.NFCPushTarget.ANY) ||
+          (this.push_options_.target === target))) {
+        this.cancelPendingPushOperation();
+      }
+
+      return createNFCError(null);
+    }
+
+    setClient(client) {
+      this.client_ = client;
+    }
+
+    async watch(options, id) {
+      assert_true(id > 0);
+      let error = this.getHWError();
+      if (error) {
+        return error;
+      }
+
+      this.watchers_.push({id: id, options: options});
+      // Triggers onWatch if the new watcher matches existing messages
+      if (this.reading_messages_.length !== 0) {
+        for(let message of this.reading_messages_) {
+          if (matchesWatchOptions(message.message,
+              message.compatibility, options)) {
+            this.client_.onWatch([id], fake_tag_serial_number,
+                toMojoNDEFMessage(message.message));
+          }
+        }
+      }
+
+      return createNFCError(null);
+    }
+
+    async cancelWatch(id) {
+      let index = this.watchers_.findIndex(value => value.id === id);
+      if (index === -1) {
+        return createNFCError(device.mojom.NFCErrorType.NOT_FOUND);
+      }
+
+      this.watchers_.splice(index, 1);
+      return createNFCError(null);
+    }
+
+    async cancelAllWatches() {
+      if (this.watchers_.length === 0) {
+        return createNFCError(device.mojom.NFCErrorType.NOT_FOUND);
+      }
+
+      this.watchers_.splice(0, this.watchers_.length);
+      return createNFCError(null);
+    }
+
+    getHWError() {
+      if (this.hw_status_ === NFCHWStatus.DISABLED)
+        return createNFCError(device.mojom.NFCErrorType.NOT_READABLE);
+      if (this.hw_status_ === NFCHWStatus.NOT_SUPPORTED)
+        return createNFCError(device.mojom.NFCErrorType.NOT_SUPPORTED);
+      return null;
+    }
+
+    setHWStatus(status) {
+      this.hw_status_ = status;
+    }
+
+    pushedMessage() {
+      return this.pushed_message_;
+    }
+
+    pushOptions() {
+      return this.push_options_;
+    }
+
+    watchOptions() {
+      assert_not_equals(this.watchers_.length, 0);
+      return this.watchers_[this.watchers_.length - 1].options;
+    }
+
+    setPendingPushCompleted(result) {
+      this.push_completed_ = result;
+    }
+
+    reset() {
+      this.hw_status_ = NFCHWStatus.ENABLED;
+      this.push_completed_ = true;
+      this.watchers_ = [];
+      this.reading_messages_ = [];
+      this.cancelPendingPushOperation();
+      this.bindingSet_.closeAllBindings();
+      this.interceptor_.stop();
+    }
+
+    cancelPendingPushOperation() {
+      if (this.pending_promise_func_) {
+        this.pending_promise_func_(
+            createNFCError(device.mojom.NFCErrorType.OPERATION_CANCELLED));
+      }
+
+      this.pushed_message_ = null;
+      this.push_options_ = null;
+      this.pending_promise_func_ = null;
+      this.push_should_timeout_ = false;
+    }
+
+    // Sets message that is used to deliver NFC reading updates
+    // with a specific NDEFCompatibility.
+    setReadingMessage(message, compatibility = 'nfc-forum') {
+      this.reading_messages_.push({message: message,
+          compatibility: toMojoNDEFCompatibility(compatibility)});
+      // Triggers onWatch if the new message matches existing watchers
+      if (this.watchers_.length !== 0) {
+        for (let watcher of this.watchers_) {
+          if (matchesWatchOptions(message,
+              message.compatibility, watcher.options)) {
+            this.client_.onWatch([watcher.id], fake_tag_serial_number,
+                toMojoNDEFMessage(message.message));
+          }
+        }
+      }
+    }
+
+    setPushShouldTimeout(result) {
+      this.push_should_timeout_ = result;
+    }
+  }
+
+  let testInternal = {
+    initialized: false,
+    mockNFC: null
+  }
+
+  class NFCTestChromium {
+    constructor() {
+      Object.freeze(this); // Make it immutable.
+    }
+
+    initialize() {
+      if (testInternal.initialized)
+        throw new Error('Call reset() before initialize().');
+
+      testInternal.mockNFC = new MockNFC;
+      testInternal.initialized = true;
+    }
+    // Resets state of nfc mocks between test runs.
+    async reset() {
+      if (!testInternal.initialized)
+        throw new Error('Call initialize() before reset().');
+      testInternal.mockNFC.reset();
+      testInternal.mockNFC = null;
+      testInternal.initialized = false;
+
+      await new Promise(resolve => setTimeout(resolve, 0));
+    }
+
+    getMockNFC() {
+      return testInternal.mockNFC;
+    }
+  }
+
+  return NFCTestChromium;
+})();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: DocumentOrShadowRoot.activeElement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script>
+function createChildAndFocus(focusParent) {
+  const focused = document.createElement("div");
+  focused.tabIndex = 0;
+  focusParent.appendChild(focused);
+  focused.focus();
+  return focused;
+}
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+}, "activeElement on document & shadow root when focused element is in the shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+
+  const focused = createChildAndFocus(document.body);
+  assert_equals(document.activeElement, focused);
+  assert_equals(shadowRoot.activeElement, null);
+}, "activeElement on document & shadow root when focused element is in the document");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(document.createElement("slot"));
+  document.body.appendChild(host);
+
+  // Child of |host|, will be slotted to the slot in |shadowRoot|.
+  const focused = createChildAndFocus(host);
+  assert_equals(document.activeElement, focused);
+  assert_equals(shadowRoot.activeElement, null);
+}, "activeElement on document & shadow root when focused element is slotted");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const neighborHost = document.createElement("div");
+  const neighborShadowRoot = neighborHost.attachShadow({ mode: "open" });
+  document.body.appendChild(neighborHost);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+  assert_equals(neighborShadowRoot.activeElement, null);
+}, "activeElement on a neighboring host when focused element is in another shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const nestedHost = document.createElement("div");
+  const nestedShadowRoot = nestedHost.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(nestedHost);
+
+  const focused = createChildAndFocus(nestedShadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, nestedHost);
+  assert_equals(nestedShadowRoot.activeElement, focused);
+}, "activeElement when focused element is in a nested shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const nestedHost = document.createElement("div");
+  const nestedShadowRoot = nestedHost.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(nestedHost);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+  assert_equals(nestedShadowRoot.activeElement, null);
+}, "activeElement when focused element is in a parent shadow tree");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and negative tabindex in shadow scope</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=-1>
+//      <slot #slotAbove tabindex=-1>
+//        (slotted) <div #slottedAbove tabindex=-1>
+//      <slot #slotBelow tabindex=-1>
+//        (slotted) <div #slottedBelow tabindex=-1>
+//      <div #belowSlot tabindex=-1>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, -1);
+  setTabIndex([aboveHost, host, belowHost], 0);
+  resetFocus();
+  return assertFocusOrder([aboveHost, host, belowHost]);
+}, "Order when all elements in shadow tree has negative tabindex");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0 except for one of the slot</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=1>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  slotBelow.tabIndex = 1;
+  resetFocus();
+  // Focus should move first according to flat tree order to #aboveHost and #host, then into #host's focus scope.
+  // It will then move to #slottedBelow because #slotBelow has tabindex=1 (though we actually won't focus on the slot),
+  // and then back to #host's focus scope again, finally getting out to the document focus scope.
+  return assertFocusOrder([aboveHost, host, slottedBelow, aboveSlot, slottedAbove, belowSlot, belowHost]);
+}, "Order when all tabindex=0, except for one slot that has tabindex=1");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom with varying tabindex values</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=3>
+// <div #host tabindex=3>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=2>
+//      <slot #slotAbove tabindex=1>
+//        (slotted) <div #slottedAbove tabindex=4>
+//      <slot #slotBelow tabindex=1>
+//        (slotted) <div #slottedBelow tabindex=4>
+//      <div #belowSlot tabindex=2>
+// <div #belowHost tabindex=3>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex([slotAbove, slotBelow], 1);
+  setTabIndex([aboveSlot, belowSlot], 2);
+  setTabIndex([aboveHost, host, belowHost], 3);
+  setTabIndex([slottedAbove, slottedBelow], 4);
+  resetFocus();
+  return assertFocusOrder([aboveHost, host, slottedAbove, slottedBelow, aboveSlot, belowSlot, belowHost]);
+}, "Order with various tabindex values");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and negative host tabindex</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=-1>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  host.tabIndex = -1;
+  resetFocus();
+  // Focus willl only move within the focus navigation scope of the document (not going to get into #host).
+  return assertFocusOrder([aboveHost, belowHost]);
+}, "Order when all tabindex=0 except for host, which has tabindex=-1");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and non-focusable host</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  removeTabIndex([host]);
+  resetFocus();
+  // Focus should move in flat tree order since every one of them has tabindex ==0,
+  // but doesn't include #slot since it's not rendered and #host since its tabindex is not set
+  // (but #host is considered as 0 in focus scope navigation, keeping the flat tree order for the shadow root's descendants).
+  return assertFocusOrder(elementsInFlatTreeOrder.filter(el => (el !== slotAbove && el !== slotBelow && el !== host)));
+}, "Order when all tabindex=0 except host (tabindex not set)");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0 except host (tabindex=1)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=1>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  host.tabIndex = 1;
+  resetFocus();
+  // Focus should move first to #host because it has tabindex=1, and then to the contents of its scope
+  // (e.g. the contents of its shadow tree) in flat tree order, and then outside to the document scope
+  // again (aboveHost & belowHost).
+  return assertFocusOrder([host, aboveSlot, slottedAbove, slottedBelow, belowSlot, aboveHost, belowHost]);
+}, "Order when all tabindex=0 except for host, which has tabindex=1");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  resetFocus();
+  // Focus should move in flat tree order since every one of them has tabindex==0,
+  // but doesn't include slots.
+  return assertFocusOrder(elementsInFlatTreeOrder.filter(el => (el !== slotAbove && el !== slotBelow)));
+}, "Order when all tabindex=0 is and delegatesFocus = false");
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/shadow-dom/focus/resources/shadow-utils.js
@@ -0,0 +1,79 @@
+// Structure:
+// <div #aboveHost>
+// <div #host>
+//    #shadowRoot
+//      <div #aboveSlot>
+//      <slot #slotAbove>
+//        (slotted) <div #slottedAbove>
+//      <slot #slotBelow>
+//        (slotted) <div #slottedBelow>
+//      <div #belowSlot>
+// <div #belowHost>
+function prepareDOM(container, delegatesFocus) {
+
+  const aboveHost = document.createElement("div");
+  aboveHost.innerText = "aboveHost";
+  const host = document.createElement("div");
+  host.id = "host";
+  const slottedBelow = document.createElement("div");
+  slottedBelow.innerText = "slotted below";
+  slottedBelow.slot = "below";
+  const slottedAbove = document.createElement("div");
+  slottedAbove.innerText = "slotted above";
+  slottedAbove.slot = "above";
+
+  const belowHost = document.createElement("div");
+  belowHost.innerText = "belowHost";
+  container.appendChild(aboveHost);
+  container.appendChild(host);
+  container.appendChild(belowHost);
+  host.appendChild(slottedBelow);
+  host.appendChild(slottedAbove);
+  const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: delegatesFocus});
+  const aboveSlot = document.createElement("div");
+  aboveSlot.innerText = "aboveSlot";
+
+  const slotAbove = document.createElement("slot");
+  slotAbove.name = "above";
+  const slotBelow = document.createElement("slot");
+  slotBelow.name = "below";
+
+  const belowSlot = document.createElement("div");
+  belowSlot.innerText = "belowSlot";
+  shadowRoot.appendChild(aboveSlot);
+  shadowRoot.appendChild(slotAbove);
+  shadowRoot.appendChild(slotBelow);
+  shadowRoot.appendChild(belowSlot);
+
+  return [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost];
+}
+
+function setTabIndex(elements, value) {
+  for (const el of elements) {
+    el.tabIndex = value;
+  }
+}
+
+function removeTabIndex(elements) {
+  for (const el of elements) {
+    el.removeAttribute("tabindex");
+  }
+}
+
+function resetFocus() {
+  document.body.focus();
+}
+
+function navigateFocusForward() {
+  // TAB = '\ue004'
+  return test_driver.send_keys(document.body, "\ue004");
+}
+
+async function assertFocusOrder(expectedOrder) {
+  const shadowRoot = document.getElementById("host").shadowRoot;
+  for (const el of expectedOrder) {
+    await navigateFocusForward();
+    const focused = shadowRoot.activeElement ? shadowRoot.activeElement : document.activeElement;
+    assert_equals(focused, el);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/sms/README.md
@@ -0,0 +1,3 @@
+# SMS Receiver API
+
+This directory contains Web platform tests of the SMS Receiver API. For more details, refer to [this README file](https://cs.chromium.org/chromium/src/content/browser/sms/README.md).
--- a/testing/web-platform/tests/streams/readable-streams/patched-global.any.js
+++ b/testing/web-platform/tests/streams/readable-streams/patched-global.any.js
@@ -102,8 +102,40 @@ promise_test(async t => {
     break;
   }
 
   // should be able to acquire a new reader
   const reader = oldReadableStreamGetReader.call(rs);
   // stream should be cancelled
   await reader.closed;
 }, 'ReadableStream getIterator() should use the original values of getReader() and ReadableStreamDefaultReader methods');
+
+test(t => {
+  const oldPromiseThen = Promise.prototype.then;
+  Promise.prototype.then = () => {
+    throw new Error('patched then() called');
+  };
+  t.add_cleanup(() => {
+    Promise.prototype.then = oldPromiseThen;
+  });
+  const [branch1, branch2] = new ReadableStream().tee();
+  assert_true(isReadableStream(branch1), 'branch1 should be a ReadableStream');
+  assert_true(isReadableStream(branch2), 'branch2 should be a ReadableStream');
+}, 'tee() should not call Promise.prototype.then()');
+
+test(t => {
+  const oldPromiseThen = Promise.prototype.then;
+  Promise.prototype.then = () => {
+    throw new Error('patched then() called');
+  };
+  t.add_cleanup(() => {
+    Promise.prototype.then = oldPromiseThen;
+  });
+  let readableController;
+  const rs = new ReadableStream({
+    start(c) {
+      readableController = c;
+    }
+  });
+  const ws = new WritableStream();
+  rs.pipeTo(ws);
+  readableController.close();
+}, 'pipeTo() should not call Promise.prototype.then()');
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/accumulate-values-width-animation.html
@@ -0,0 +1,110 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>This tests values animation and accumulate='sum'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<!-- an1: Change width to 100 in 10s with a wobbling animation -->
+<rect width="20" height="100" fill="green">
+    <animate id="an1" attributeName="width" dur="2s" values="0; 30; 20" accumulate="sum" repeatCount="5" fill="freeze"/>
+</rect>
+
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.width.animVal.value, 0, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample2() {
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample3() {
+    assert_approx_equals(rect.width.animVal.value, 20, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample4() {
+    assert_approx_equals(rect.width.animVal.value, 50, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample5() {
+    assert_approx_equals(rect.width.animVal.value, 40, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample6() {
+    assert_approx_equals(rect.width.animVal.value, 70, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample7() {
+    assert_approx_equals(rect.width.animVal.value, 60, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample8() {
+    assert_approx_equals(rect.width.animVal.value, 90, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample9() {
+    assert_approx_equals(rect.width.animVal.value, 80, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample10() {
+    assert_approx_equals(rect.width.animVal.value, 110, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+function sample11() {
+    assert_approx_equals(rect.width.animVal.value, 100, epsilon);
+    assert_equals(rect.width.baseVal.value, 20);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 1.0,    sample2],
+        ["an1", 1.999,  sample3],
+        ["an1", 2.001,  sample3],
+        ["an1", 3.0,    sample4],
+        ["an1", 3.999,  sample5],
+        ["an1", 4.001,  sample5],
+        ["an1", 5.0,    sample6],
+        ["an1", 5.999,  sample7],
+        ["an1", 6.001,  sample7],
+        ["an1", 7.0,    sample8],
+        ["an1", 7.999,  sample9],
+        ["an1", 8.001,  sample9],
+        ["an1", 9.0,    sample10],
+        ["an1", 9.999,  sample11],
+        ["an1", 10.001, sample11],
+        ["an1", 11.0,   sample11],
+        ["an1", 60.0,   sample11]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/additive-from-to-width-animation.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>This tests multiple additive='sum' animations running at the same time</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<!-- an1: Change width from 10 to 50 in 4s -->
+<!-- an2: Change width from 10 to 25 + additive=sum. This results in a change from 20 to 75 -->
+<!-- an3: Change width from 0 to 25 + additve=sum. This results in a final change from 20 to 100 -->
+<rect width="10" height="100" fill="green">
+    <animate id="an1" attributeType="XML" attributeName="width" fill="freeze" from="10" to="50" begin="0s" dur="4s"/>
+    <animate id="an2" attributeType="XML" attributeName="width" additive="sum" fill="freeze" from="10" to="25" begin="0s" dur="4s"/>
+    <animate id="an3" attributeType="XML" attributeName="width" additive="sum" fill="freeze" from="0" to="25" begin="0s" dur="4s"/>
+</rect>
+
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.width.animVal.value, 20, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+function sample2() {
+    assert_approx_equals(rect.width.animVal.value, 60, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+function sample3() {
+    assert_approx_equals(rect.width.animVal.value, 100, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // All animations in the test file use the same duration, so it's not needed to list all sample points individually for an5/an6/an7/an8.
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,  sample1],
+        ["an1", 2.0,  sample2],
+        ["an1", 4.0,  sample3],
+        ["an1", 60.0, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/additive-type-by-animation.html
@@ -0,0 +1,275 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>This by animation for all XML property types</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg id="svg" viewBox="0 0 300 300" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<!-- All additive types except AnimatedPath are tested here -->
+<defs>
+    <marker id="marker" viewBox="0 0 10 10" markerWidth="4" markerHeight="3" refX="1" refY="5" orient="-45deg">
+        <polyline id="polyline" points="0,0 10,5 0,10 1,5" fill="green"/>
+    </marker>
+
+    <filter id="filter">
+        <feConvolveMatrix id="feConvolveMatrix" kernelUnitLength="20 30" kernelMatrix="0 1 0   0 1 0   0 1 0" divisor="37.5" order="6 6" targetX="5" preserveAlpha="false"/>
+    </filter>
+</defs>
+
+<!-- Non-additive types AnimatedBoolean, AnimatedEnumeration, AnimatedPreserveAspectRatio, AnimatedString are tested in non-additive-type-by-animation.svg -->
+
+<path id="path" d="M45,50 L55,50" transform="rotate(45)" stroke-width="10" stroke="green" marker-end="url(#marker)"/>
+<rect id="rect" y="0" width="100" height="100" fill="black" filter="url(#filter)"/>
+<text id="text" x="50" y="50" dy="5 -10 10 -10">ABCD</text>
+
+<!-- AnimatedAngle -->
+<animate id="an1" xlink:href="#marker" attributeName="orient" begin="0s" dur="4s" by="45deg" fill="freeze"/>
+
+<!-- AnimatedColor -->
+<animate xlink:href="#rect" attributeName="fill" begin="0s" dur="4s" by="green" fill="freeze"/>
+
+<!-- AnimatedLength -->
+<animate xlink:href="#rect" attributeName="y" begin="0s" dur="4s" by="100" fill="freeze"/>
+
+<!-- AnimatedLengthList -->
+<animate xlink:href="#text" attributeName="dy" begin="0s" dur="4s" by="-10 20 -20 20" fill="freeze"/>
+
+<!-- AnimatedNumberOptionalNumber -->
+<animate xlink:href="#feConvolveMatrix" attributeName="kernelUnitLength" begin="0s" dur="4s" by="-10 -20" fill="freeze"/>
+
+<!-- AnimatedNumber -->
+<animate xlink:href="#feConvolveMatrix" attributeName="divisor" begin="0s" dur="4s" by="-17.5" fill="freeze"/>
+
+<!-- AnimatedNumberList -->
+<animate xlink:href="#feConvolveMatrix" attributeName="kernelMatrix" begin="0s" dur="4s" by="2 1 3   2 1 3   2 1 3" fill="freeze"/>
+
+<!-- AnimatedIntegerOptionalInteger -->
+<animate xlink:href="#feConvolveMatrix" attributeName="order" begin="0s" dur="4s" by="-3 -3" fill="freeze"/>
+
+<!-- AnimatedInteger -->
+<animate xlink:href="#feConvolveMatrix" attributeName="targetX" begin="0s" dur="4s" by="-4" fill="freeze"/>
+
+<!-- AnimatedPoints -->
+<animate xlink:href="#polyline" attributeName="points" begin="0s" dur="4s" by="0,0 10,5 0,10 1,5" fill="freeze"/>
+
+<!-- AnimatedRect -->
+<animate xlink:href="#svg" attributeName="viewBox" begin="0s" dur="4s" by="0 0 -100 -100" fill="freeze"/>
+
+<!-- AnimatedTransformList -->
+<animateTransform xlink:href="#path" attributeName="transform" type="rotate" begin="0s" dur="4s" by="-45" fill="freeze"/>
+
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function checkBaseVal() {
+    assert_equals(marker.orientAngle.baseVal.value, -45);
+    assert_equals(feConvolveMatrix.divisor.baseVal, 37.5);
+    assert_equals(feConvolveMatrix.orderX.baseVal, 6);
+    assert_equals(feConvolveMatrix.orderY.baseVal, 6);
+    assert_equals(feConvolveMatrix.targetX.baseVal, 5);
+    assert_equals(feConvolveMatrix.kernelUnitLengthX.baseVal, 20);
+    assert_equals(feConvolveMatrix.kernelUnitLengthY.baseVal, 30);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.numberOfItems, 9);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(0).value, 0);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(1).value, 1);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(2).value, 0);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(3).value, 0);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(4).value, 1);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(5).value, 0);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(6).value, 0);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(7).value, 1);
+    assert_equals(feConvolveMatrix.kernelMatrix.baseVal.getItem(8).value, 0);
+    assert_equals(rect.y.baseVal.value, 0);
+    assert_equals(text.dy.baseVal.numberOfItems, 4);
+    assert_equals(text.dy.baseVal.getItem(0).value, 5);
+    assert_equals(text.dy.baseVal.getItem(1).value, -10);
+    assert_equals(text.dy.baseVal.getItem(2).value, 10);
+    assert_equals(text.dy.baseVal.getItem(3).value, -10);
+    assert_equals(svg.viewBox.baseVal.x, 0);
+    assert_equals(svg.viewBox.baseVal.y, 0);
+    assert_equals(svg.viewBox.baseVal.width, 300);
+    assert_equals(svg.viewBox.baseVal.height, 300);
+    assert_equals(polyline.points.numberOfItems, 4);
+    assert_equals(polyline.points.getItem(0).x, 0);
+    assert_equals(polyline.points.getItem(0).y, 0);
+    assert_equals(polyline.points.getItem(1).x, 10);
+    assert_equals(polyline.points.getItem(1).y, 5);
+    assert_equals(polyline.points.getItem(1).x, 10);
+    assert_equals(polyline.points.getItem(1).y, 5);
+    assert_equals(polyline.points.getItem(2).x, 0);
+    assert_equals(polyline.points.getItem(2).y, 10);
+    assert_equals(path.transform.baseVal.numberOfItems, 1);
+    assert_equals(path.transform.baseVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_equals(path.transform.baseVal.getItem(0).angle, 45);
+}
+
+function sample1() {
+    assert_approx_equals(marker.orientAngle.animVal.value, -45, epsilon);
+    assert_approx_equals(feConvolveMatrix.divisor.animVal, 37.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderX.animVal, 6, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderY.animVal, 6, epsilon);
+    assert_approx_equals(feConvolveMatrix.targetX.animVal, 5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthX.animVal, 20, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthY.animVal, 30, epsilon);
+    assert_equals(feConvolveMatrix.kernelMatrix.animVal.numberOfItems, 9);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(0).value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(1).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(2).value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(3).value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(4).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(5).value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(6).value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(7).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(8).value, 0, epsilon);
+    assert_approx_equals(rect.y.animVal.value, 0, epsilon);
+    assert_equals(text.dy.animVal.numberOfItems, 4);
+    assert_approx_equals(text.dy.animVal.getItem(0).value, 5, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(1).value, -10, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(2).value, 10, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(3).value, -10, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.x, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.y, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.width, 300, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.height, 300, epsilon);
+    assert_equals(polyline.animatedPoints.numberOfItems, 4);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).y, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 10, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 5, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 10, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 5, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).y, 10, epsilon);
+    assert_equals(path.transform.animVal.numberOfItems, 2);
+    assert_equals(path.transform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_equals(path.transform.animVal.getItem(0).angle, 45);
+    assert_equals(path.transform.animVal.getItem(1).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_approx_equals(path.transform.animVal.getItem(1).angle, 0, epsilon);
+    expectFillColor(rect, 0, 0, 0);
+    checkBaseVal();
+}
+
+function sample2() {
+    assert_approx_equals(marker.orientAngle.animVal.value, -22.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.divisor.animVal, 28.75, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderX.animVal, 5, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderY.animVal, 5, epsilon);
+    assert_approx_equals(feConvolveMatrix.targetX.animVal, 3, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthX.animVal, 15, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthY.animVal, 20, epsilon);
+    assert_equals(feConvolveMatrix.kernelMatrix.animVal.numberOfItems, 9);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(0).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(1).value, 1.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(2).value, 1.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(3).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(4).value, 1.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(5).value, 1.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(6).value, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(7).value, 1.5, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(8).value, 1.5, epsilon);
+    assert_approx_equals(rect.y.animVal.value, 50, epsilon);
+    assert_equals(text.dy.animVal.numberOfItems, 4);
+    assert_approx_equals(text.dy.animVal.getItem(0).value, 0, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(1).value, 0, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(2).value, 0, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(3).value, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.x, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.y, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.width, 250, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.height, 250, epsilon);
+    assert_equals(polyline.animatedPoints.numberOfItems, 4);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).y, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 15, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 7.5, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 15, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 7.5, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).y, 15, epsilon);
+    assert_equals(path.transform.animVal.numberOfItems, 2);
+    assert_equals(path.transform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_equals(path.transform.animVal.getItem(0).angle, 45);
+    assert_equals(path.transform.animVal.getItem(1).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_approx_equals(path.transform.animVal.getItem(1).angle, -22.5, epsilon);
+    expectFillColor(rect, 0, 63, 0);
+    checkBaseVal();
+}
+
+function sample3() {
+    assert_approx_equals(marker.orientAngle.animVal.value, 0, epsilon);
+    assert_approx_equals(feConvolveMatrix.divisor.animVal, 20, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderX.animVal, 3, epsilon);
+    assert_approx_equals(feConvolveMatrix.orderY.animVal, 3, epsilon);
+    assert_approx_equals(feConvolveMatrix.targetX.animVal, 1, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthX.animVal, 10, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelUnitLengthY.animVal, 10, epsilon);
+    assert_equals(feConvolveMatrix.kernelMatrix.animVal.numberOfItems, 9);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(0).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(1).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(2).value, 3, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(3).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(4).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(5).value, 3, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(6).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(7).value, 2, epsilon);
+    assert_approx_equals(feConvolveMatrix.kernelMatrix.animVal.getItem(8).value, 3, epsilon);
+    assert_approx_equals(rect.y.animVal.value, 100, epsilon);
+    assert_equals(text.dy.animVal.numberOfItems, 4);
+    assert_approx_equals(text.dy.animVal.getItem(0).value, -5, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(1).value, 10, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(2).value, -10, epsilon);
+    assert_approx_equals(text.dy.animVal.getItem(3).value, 10, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.x, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.y, 0, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.width, 200, epsilon);
+    assert_approx_equals(svg.viewBox.animVal.height, 200, epsilon);
+    assert_equals(polyline.animatedPoints.numberOfItems, 4);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(0).y, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 20, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 10, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).x, 20, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(1).y, 10, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).x, 0, epsilon);
+    assert_approx_equals(polyline.animatedPoints.getItem(2).y, 20, epsilon);
+    assert_equals(path.transform.animVal.numberOfItems, 2);
+    assert_equals(path.transform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_equals(path.transform.animVal.getItem(0).angle, 45);
+    assert_equals(path.transform.animVal.getItem(1).type, SVGTransform.SVG_TRANSFORM_ROTATE);
+    assert_approx_equals(path.transform.animVal.getItem(1).angle, -45, epsilon);
+    expectFillColor(rect, 0, 128, 0);
+    checkBaseVal();
+}
+
+smil_async_test((t) => {
+    marker = rootSVGElement.ownerDocument.getElementsByTagName("marker")[0];
+    filter = rootSVGElement.ownerDocument.getElementsByTagName("filter")[0];
+    feConvolveMatrix = rootSVGElement.ownerDocument.getElementsByTagName("feConvolveMatrix")[0];
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+    svg = rootSVGElement.ownerDocument.getElementsByTagName("svg")[0];
+    path = rootSVGElement.ownerDocument.getElementsByTagName("path")[0];
+    polyline = rootSVGElement.ownerDocument.getElementsByTagName("polyline")[0];
+    text = rootSVGElement.ownerDocument.getElementsByTagName("text")[0];
+
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,   sample1],
+        ["an1", 2.0,   sample2],
+        ["an1", 4.001, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/additive-values-width-animation.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>This tests values animation and additive='sum'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<!-- an1: Change width from 0 to 50 to 100, all linear interpolated. As additive is set to sum it should add the current baseValue
+          to the new animated value each time a value from the values list is consumed. Expected:
+          At 0s, width=10, at 2s, width=50+10=60, at 4s, width=100+10=110.
+
+          Our testing harness will change the baseValue from 10 to 60 at 5s. The current animated value (before the script change)
+          is: <baseValue>+<animValue>: 10 + (100/6*5) = 93.333 at this point. As we change the baseValue to 60, the equation now looks like:
+          60 + (100/6*5) = 143.333. Before the script change the last second of the animation would have animated width from 85 to 110.
+          Due the script change its now animating from 93.999 to 143.333 during the last second.
+-->
+<rect width="10" height="100" fill="green">
+    <animate id="an1" attributeType="XML" attributeName="width" fill="freeze" additive="sum" values="0; 50; 100" begin="0s" dur="6s"/>
+</rect>
+
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.width.animVal.value, 10, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+function sample2() {
+    assert_approx_equals(rect.width.animVal.value, 60, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+function sample3() {
+    assert_approx_equals(rect.width.animVal.value, 93.3, epsilon);
+    assert_equals(rect.width.baseVal.value, 10);
+}
+
+function changeBaseVal() {
+    // At 5s, only change the baseVal.
+    rect.width.baseVal.value = 60;
+}
+
+function sample4() {
+    assert_approx_equals(rect.width.animVal.value, 143.33, epsilon);
+    assert_equals(rect.width.baseVal.value, 60);
+}
+
+function sample5() {
+    assert_approx_equals(rect.width.animVal.value, 160, epsilon);
+    assert_equals(rect.width.baseVal.value, 60);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // All animations in the test file use the same duration, so it's not needed to list all sample points individually for an5/an6/an7/an8.
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,   sample1],
+        ["an1", 3.0,   sample2],
+        ["an1", 4.999, sample3],
+        ["an1", 5.0,   changeBaseVal],
+        ["an1", 5.001, sample4],
+        ["an1", 6.001, sample5],
+        ["an1", 60.0,  sample5]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-calcMode-spline-by.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode spline with by animation. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("by", "-100");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;1");
+animate.setAttribute("keySplines", "0.25 .5 .25 0.85");
+animate.setAttribute("calcMode", "spline");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.x.animVal.value, 18.8, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 2.0,   sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-calcMode-spline-from-by.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode spline with from-by animation. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("from", "100");
+animate.setAttribute("by", "-100");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;1");
+animate.setAttribute("keySplines", "0.25 .5 .25 0.85");
+animate.setAttribute("calcMode", "spline");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.x.animVal.value, 18.8, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 2.0,   sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-calcMode-spline-from-to.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode spline with from-to animation. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("from", "100");
+animate.setAttribute("to", "0");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;1");
+animate.setAttribute("keySplines", "0.25 .5 .25 0.85");
+animate.setAttribute("calcMode", "spline");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.x.animVal.value, 18.8, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 2.0,   sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-calcMode-spline-to.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode spline with to animation. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("to", "0");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;1");
+animate.setAttribute("keySplines", "0.25 .5 .25 0.85");
+animate.setAttribute("calcMode", "spline");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.x.animVal.value, 18.8, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 2.0,   sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-calcMode-spline-values.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode spline with values animation. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("values", "100;0");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;1");
+animate.setAttribute("keySplines", "0.25 .5 .25 0.85");
+animate.setAttribute("calcMode", "spline");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.x.animVal.value, 18.8, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 2.0,   sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-color-calcMode-discrete.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test calcMode discrete with from-to animation on colors. You should see a green 100x100 rect and only PASS messages</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("color", "rgb(128,255,255)");
+rect.setAttribute("fill", "currentColor");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "color");
+animate.setAttribute("from", "rgb(255,0,0)");
+animate.setAttribute("to", "rgb(0,255,255)");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("calcMode", "discrete");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    expectFillColor(rect, 128, 255, 255);
+}
+
+function sample2() {
+    expectFillColor(rect, 255, 0, 0);
+}
+
+function sample3() {
+    expectFillColor(rect, 0, 255, 255);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 0.001, sample2],
+        ["animation", 1.999, sample2],
+        ["animation", 2.001, sample3],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample1]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-color-fill-currentColor.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests animation on 'currentColor'.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "100px");
+rect.setAttribute("height", "100px");
+rect.setAttribute("fill", "currentColor");
+rect.setAttribute("color", "red");
+rect.setAttribute("onclick", "executeTest()");
+
+var animateColor = createSVGElement("animate");
+animateColor.setAttribute("id", "animateColor");
+animateColor.setAttribute("attributeName", "color");
+animateColor.setAttribute("from", "red");
+animateColor.setAttribute("to", "green");
+animateColor.setAttribute("dur", "3s");
+animateColor.setAttribute("begin", "0s");
+animateColor.setAttribute("fill", "freeze");
+rect.appendChild(animateColor);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+
+    expectFillColor(rect, 255, 0, 0);
+}
+
+function sample2() {
+
+    expectFillColor(rect, 128, 64, 0);
+}
+
+function sample3() {
+
+    expectFillColor(rect, 0, 128, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animateColor", 0.0, sample1],
+        ["animateColor", 1.5, sample2],
+        ["animateColor", 3.0, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-color-fill-from-by.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests animation on 'currentColor'.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "100px");
+rect.setAttribute("height", "100px");
+rect.setAttribute("fill", "currentColor");
+rect.setAttribute("color", "#d00000");
+rect.setAttribute("onclick", "executeTest()");
+
+var animateColor = createSVGElement("animate");
+animateColor.setAttribute("id", "animateColor");
+animateColor.setAttribute("attributeName", "color");
+animateColor.setAttribute("from", "#d00000");
+animateColor.setAttribute("by", "#0000d0");
+animateColor.setAttribute("dur", "3s");
+animateColor.setAttribute("begin", "0s");
+animateColor.setAttribute("fill", "freeze");
+rect.appendChild(animateColor);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+
+    expectFillColor(rect, 208, 0, 0);
+}
+
+function sample2() {
+
+    expectFillColor(rect, 208, 0, 104);
+}
+
+function sample3() {
+
+    expectFillColor(rect, 208, 0, 208);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animateColor", 0.0, sample1],
+        ["animateColor", 1.5, sample2],
+        ["animateColor", 3.0, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-color-transparent.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests that 'transparent' is treated as a valid color.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "test");
+rect.setAttribute("width", "100px");
+rect.setAttribute("height", "100px");
+rect.setAttribute("fill", "#00FF00");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "fill");
+animate.setAttribute("from", "transparent");
+animate.setAttribute("by", "red");
+animate.setAttribute("dur", "3s");
+animate.setAttribute("begin", "0s");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function expectTransparent() {
+    expectFillColor(rect, 0, 255, 0);
+}
+
+function expectOtherColor() {
+    expectFillColor(rect, 127, 0, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0, expectTransparent],
+        ["animation", 1.5, expectOtherColor]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-css-xml-attributeType.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests that XML and CSS attributeTypes can be switched between.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var polygon = createSVGElement("polygon");
+polygon.setAttribute("id", "polygon");
+polygon.setAttribute("points", "100 0 200 0 200 100 100 100");
+polygon.setAttribute("fill", "green");
+polygon.setAttribute("onclick", "executeTest()");
+
+var set = createSVGElement("set");
+set.setAttribute("id", "set");
+set.setAttribute("attributeName", "points");
+set.setAttribute("attributeType", "XML");
+set.setAttribute("to", "300 0 400 0 400 100 300 100");
+set.setAttribute("begin", "0s");
+polygon.appendChild(set);
+rootSVGElement.appendChild(polygon);
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(polygon.animatedPoints.getItem(0).x, 100, epsilon);
+    assert_equals(polygon.points.getItem(0).x, 100);
+}
+
+function sample2() {
+    assert_approx_equals(polygon.animatedPoints.getItem(0).x, 300, epsilon);
+    // change the animationType to CSS which is invalid.
+    set.setAttribute("attributeType", "CSS");
+}
+
+function sample3() {
+    // verify that the animation resets.
+    assert_approx_equals(polygon.animatedPoints.getItem(0).x, 100, epsilon);
+    // change the animation to a CSS animatable value.
+    set.setAttribute("attributeName", "opacity");
+    set.setAttribute("to", "0.8");
+}
+
+function sample4() {
+    assert_approx_equals(parseFloat(getComputedStyle(polygon).opacity), 0.8, epsilon);
+    // change the animation to a non-CSS animatable value.
+    set.setAttribute("attributeName", "points");
+    set.setAttribute("to", "200 0 300 0 300 100 200 100");
+}
+
+function sample5() {
+    // verify that the animation does not run.
+    assert_approx_equals(polygon.animatedPoints.getItem(0).x, 100, epsilon);
+    assert_approx_equals(parseFloat(getComputedStyle(polygon).opacity), 1.0, epsilon);
+    // change the animationType to XML which is valid.
+    set.setAttribute("attributeType", "XML");
+}
+
+function sample6() {
+    assert_approx_equals(polygon.animatedPoints.getItem(0).x, 200, epsilon);
+    assert_equals(polygon.points.getItem(0).x, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["set", 0.0, sample1],
+        ["set", 0.5, sample2],
+        ["set", 1.0, sample3],
+        ["set", 1.5, sample4],
+        ["set", 2.0, sample5],
+        ["set", 2.5, sample6]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-currentColor.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests animation on 'currentColor'.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "100px");
+rect.setAttribute("height", "100px");
+rect.setAttribute("fill", "red");
+rect.setAttribute("color", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animateCurrentColor = createSVGElement("animate");
+animateCurrentColor.setAttribute("id", "animateCurrentColor");
+animateCurrentColor.setAttribute("attributeName", "fill");
+animateCurrentColor.setAttribute("from", "red");
+animateCurrentColor.setAttribute("to", "currentColor");
+animateCurrentColor.setAttribute("dur", "3s");
+animateCurrentColor.setAttribute("begin", "0s");
+animateCurrentColor.setAttribute("fill", "freeze");
+rect.appendChild(animateCurrentColor);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial conditions
+    expectFillColor(rect, 255, 0, 0);
+}
+
+function sample2() {
+    // Check half-time conditions
+    expectFillColor(rect, 128, 64, 0);
+}
+
+function sample3() {
+    // Check end condition
+    expectFillColor(rect, 0, 128, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animateCurrentColor", 0.0, sample1],
+        ["animateCurrentColor", 1.5, sample2],
+        ["animateCurrentColor", 3.0, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-dynamic-update-attributeName.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test behavior on dynamic-update of attributeName</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "200");
+rect.setAttribute("height", "200");
+rect.setAttribute("fill", "red");
+rect.setAttribute("color", "red");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "color");
+animate.setAttribute("from", "green");
+animate.setAttribute("to", "green");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "3s");
+animate.setAttribute("fill", "freeze");
+animate.setAttribute("calcMode", "discrete");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    expectColor(rect, 255, 0, 0);
+    assert_equals(rect.style.color, "");
+}
+
+function sample2() {
+    expectColor(rect, 0, 128, 0);
+    assert_equals(rect.style.color, "");
+}
+
+function sample3() {
+    // Set 'attributeName' from 'color' to 'fill'
+    animate.setAttribute("attributeName", "fill");
+}
+
+function sample4() {
+    expectFillColor(rect, 0, 128, 0);
+    assert_equals(rect.style.color, "");
+    assert_equals(rect.style.fill, "");
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 0.001, sample2],
+        ["animation", 1.5,   sample3],
+        ["animation", 3.0,   sample4],
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-02-t-drt.html
@@ -0,0 +1,183 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-09.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-02-t.svg                       -->
+<!--                                                                      -->
+<!-- Test 'additive' and 'accumulate' attributes.                         -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Rick Graham Feb/05/2002                            -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg version="1.1" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root"
+     width="100%" height="100%" viewBox="0 0 480 360">
+    <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+<!--nav data here-->
+        <OperatorScript version="$Revision: 1.8 $" testname="animate-elem-02-t.svg">
+            <Paragraph>
+                Test 'additive' and 'accumulate' attributes.
+            </Paragraph>
+            <Paragraph>
+                The four pictures show the effect with the four possible combinations of
+                'additive' (either 'replace' or 'sum') and 'accumulate' (either 'none' or 'sum').
+                Because two animations are animating the height, the effects of 'additive' and
+                'accumulate' are sometimes different than when there is only a single animation.
+            </Paragraph>
+        </OperatorScript>
+    </SVGTestCase>
+    <title id="test-title">animate-elem-02-t</title>
+    <desc id="test-desc">Test 'additive' and 'accumulate' attributes.</desc>
+    <!--======================================================================-->
+    <!--Content of Test Case follows...                  =====================-->
+    <!--======================================================================-->
+    <g id="test-body-content" font-family="Arial" font-size="30">
+
+            <g transform="translate(0, 0)">
+                <rect x="60" y="20" width="50" height="200" fill="#dfdfdf" stroke="#dfdfdf" stroke-width="4"/>
+                <line x1="40" x2="100" y1="220" y2="220" fill="none" stroke="#880000" stroke-width="4"/>
+                <line x1="40" x2="100" y1="120" y2="120" fill="none" stroke="#880000" stroke-width="4"/>
+                <rect x="60" y="20" width="50" height="20" fill="#0f5" stroke="#085" stroke-width="4">
+                    <animate id="an5" attributeName="height" calcMode="discrete" additive="replace" accumulate="none"
+                     repeatCount="2" from="200" to="20" begin="0s" dur="4s" fill="freeze"/>
+                </rect>
+                <text x="30" y="285" fill="navy">anim.5</text>
+            </g>
+            <g transform="translate(110, 0)">
+                <rect x="60" y="20" width="50" height="200" fill="#dfdfdf" stroke="#dfdfdf" stroke-width="4"/>
+                <line x1="40" x2="100" y1="220" y2="220" fill="none" stroke="#880000" stroke-width="4"/>
+                <line x1="40" x2="100" y1="120" y2="120" fill="none" stroke="#880000" stroke-width="4"/>
+                <rect x="60" y="20" width="50" height="20" fill="#0f5" stroke="#085" stroke-width="4">
+                    <animate id="an6" attributeName="height" calcMode="discrete" additive="sum" accumulate="none"
+                     repeatCount="2" from="200" to="20" begin="0s" dur="4s" fill="freeze"/>
+                </rect>
+                <text x="30" y="285" fill="navy">anim.6</text>
+            </g>
+            <g transform="translate(220, 0)">
+                <rect x="60" y="20" width="50" height="200" fill="#dfdfdf" stroke="#dfdfdf" stroke-width="4"/>
+                <line x1="40" x2="100" y1="220" y2="220" fill="none" stroke="#880000" stroke-width="4"/>
+                <line x1="40" x2="100" y1="120" y2="120" fill="none" stroke="#880000" stroke-width="4"/>
+                <rect x="60" y="20" width="50" height="20" fill="#0f5" stroke="#085" stroke-width="4">
+                    <animate id="an7" attributeName="height" calcMode="discrete" additive="replace" accumulate="sum"
+                     repeatCount="2" from="200" to="20" begin="0s" dur="4s" fill="freeze"/>
+                </rect>
+                <text x="30" y="285" fill="navy">anim.7</text>
+            </g>
+            <g transform="translate(330, 0)">
+                <rect x="60" y="20" width="50" height="200" fill="#dfdfdf" stroke="#dfdfdf" stroke-width="4"/>
+                <line x1="40" x2="100" y1="220" y2="220" fill="none" stroke="#880000" stroke-width="4"/>
+                <line x1="40" x2="100" y1="120" y2="120" fill="none" stroke="#880000" stroke-width="4"/>
+                <rect x="60" y="20" width="50" height="20" fill="#0f5" stroke="#085" stroke-width="4">
+                    <animate id="an8" attributeName="height" calcMode="discrete" additive="sum" accumulate="sum"
+                     repeatCount="2" from="200" to="20" begin="0s" dur="4s" fill="freeze"/>
+                </rect>
+                <text x="30" y="285" fill="navy">anim.8</text>
+            </g>
+    <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.8 $</text>
+    <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+   </g>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sampleAfterBegin() {
+    assert_equals(rect1.height.animVal.value, 200);
+    assert_equals(rect1.height.baseVal.value, 20);
+
+    assert_equals(rect2.height.animVal.value, 220);
+    assert_equals(rect2.height.baseVal.value, 20);
+
+    assert_equals(rect3.height.animVal.value, 200);
+    assert_equals(rect3.height.baseVal.value, 20);
+}
+
+function sampleAfterMid() {
+    assert_equals(rect1.height.animVal.value, 20);
+    assert_equals(rect1.height.baseVal.value, 20);
+
+    assert_equals(rect2.height.animVal.value, 40);
+    assert_equals(rect2.height.baseVal.value, 20);
+
+    assert_equals(rect3.height.animVal.value, 20);
+    assert_equals(rect3.height.baseVal.value, 20);
+}
+
+function sampleAfterBeginOfFirstRepetition() {
+    assert_equals(rect1.height.animVal.value, 200);
+    assert_equals(rect1.height.baseVal.value, 20);
+
+    assert_equals(rect2.height.animVal.value, 220);
+    assert_equals(rect2.height.baseVal.value, 20);
+
+    assert_equals(rect3.height.animVal.value, 220);
+    assert_equals(rect3.height.baseVal.value, 20);
+}
+
+function sampleAfterMidOfFirstRepetition() {
+    assert_equals(rect1.height.animVal.value, 20);
+    assert_equals(rect1.height.baseVal.value, 20);
+
+    assert_equals(rect2.height.animVal.value, 40);
+    assert_equals(rect2.height.baseVal.value, 20);
+
+    assert_equals(rect3.height.animVal.value, 40);
+    assert_equals(rect3.height.baseVal.value, 20);
+}
+
+smil_async_test((t) => {
+    rects = rootSVGElement.ownerDocument.getElementsByTagName("rect");
+    rect1 = rects[1];
+    rect2 = rects[3];
+    rect3 = rects[5];
+    rect4 = rects[7];
+
+    // All animations in the test file use the same duration, so it's not needed to list all sample points individually for an5/an6/an7/an8.
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an5", 0.0,   sampleAfterBegin],
+        ["an5", 1.999, sampleAfterBegin],
+        ["an5", 2.0,   sampleAfterMid],
+        ["an5", 3.999, sampleAfterMid],
+        ["an5", 4.0,   sampleAfterBeginOfFirstRepetition],
+        ["an5", 5.999, sampleAfterBeginOfFirstRepetition],
+        ["an5", 6.001, sampleAfterMidOfFirstRepetition],
+        ["an5", 7.999, sampleAfterMidOfFirstRepetition],
+        ["an5", 8.001, sampleAfterMidOfFirstRepetition],
+        ["an5", 60.0,  sampleAfterMidOfFirstRepetition]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-14-t-drt.html
@@ -0,0 +1,140 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-14-t.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-07-t-a.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.6 $" testname="animate-elem-14-t.svg">
+         <Paragraph>
+            Test 'calcMode'=discrete
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the height of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+            This test shows an animation with calcMode="discrete" (i.e., a jumping animation).
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-14-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="discrete"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.2;.4;.6"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="245" y="140">2</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">4</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">6</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="discrete" values="300;255;180;30" keyTimes="0;.2;.4;.6" begin="0s" dur="10s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.6 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 2s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 2s to 4s
+    assert_approx_equals(rect.width.animVal.value, 255, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 4s to 6s
+    assert_approx_equals(rect.width.animVal.value, 180, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 6s to 10s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: keyTimes="0;.2;.4;.6" begin="0s" dur="10s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 1.999,  sample1],
+        ["an1", 2.001,  sample2],
+        ["an1", 3.999,  sample2],
+        ["an1", 4.001,  sample3],
+        ["an1", 5.999,  sample3],
+        ["an1", 6.001,  sample4],
+        ["an1", 7.001,  sample4],
+        ["an1", 8.001,  sample4],
+        ["an1", 9.001,  sample4],
+        ["an1", 9.999,  sample4],
+        ["an1", 10.001, sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-15-t-drt.html
@@ -0,0 +1,136 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-07-f.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-15-t.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.5 $" testname="animate-elem-15-t.svg">
+         <Paragraph>
+            Test 'calcMode'=paced
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the height of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+            This test shows calcMode="paced" for an animation that has constant velocity, thus showing how 'values'
+            and 'keyTimes' are ignored.
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-15-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="paced"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.25;.5;1"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="230" y="140">1.5</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">4</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">9</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="paced" values="300;255;180;30" keyTimes="0;.25;.5;1" begin="0s" dur="9s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.5 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 2.25s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 2.25s to 4.5s
+    assert_approx_equals(rect.width.animVal.value, 232.5, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 4.5s to 9s
+    assert_approx_equals(rect.width.animVal.value, 165, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 9s to 9s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: keyTimes="0;.25;.5;1" begin="0s" dur="9s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 2.249,  sample2],
+        ["an1", 2.251,  sample2],
+        ["an1", 4.499,  sample3],
+        ["an1", 4.501,  sample3],
+        ["an1", 8.999,  sample4],
+        ["an1", 9.001,  sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-16-t-drt.html
@@ -0,0 +1,136 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-07-f.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-16-t.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.1 $" testname="animate-elem-07-t-c.svg">
+         <Paragraph>
+            Test 'calcMode'=linear
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the height of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+            This test shows an animation with calcMode="linear".
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-16-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="linear"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.25;.5;1"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="245" y="140">2</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">4</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">8</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="linear" values="300;255;180;30" keyTimes="0;.25;.5;1" begin="0s" dur="8s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.1 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 2s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 2s to 4s
+    assert_approx_equals(rect.width.animVal.value, 255, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 4s to 8s
+    assert_approx_equals(rect.width.animVal.value, 180, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 8s to 8s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: keyTimes="0;.25;.5;1" begin="0s" dur="8s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 1.999,  sample2],
+        ["an1", 2.001,  sample2],
+        ["an1", 3.999,  sample3],
+        ["an1", 4.001,  sample3],
+        ["an1", 7.999,  sample4],
+        ["an1", 8.001,  sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-17-t-drt.html
@@ -0,0 +1,136 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-07-f.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-17-t.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.6 $" testname="animate-elem-17-t.svg">
+         <Paragraph>
+            Test 'calcMode'=spline
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the height of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+             This animation shows calcMode="spline". Between time 4 seconds and 8 seconds, the animation displays an ease-in/ease-out approach
+            instead of a constant linear approach which would have been the case if calcMode had been linear instead.
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-17-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="spline"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.25;.5;1"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="245" y="140">2</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">4</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">8</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="spline" values="300;255;180;30" keyTimes="0;.25;.5;1" keySplines="0,0,1,1;0,0,1,1;1,0,0,1" begin="0s" dur="8s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.6 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 2s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 2s to 4s
+    assert_approx_equals(rect.width.animVal.value, 255, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 4s to 8s
+    assert_approx_equals(rect.width.animVal.value, 180, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 8s to 8s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: keyTimes="0;.25;.5;1" keySplines="0,0,1,1;0,0,1,1;1,0,0,1" begin="0s" dur="8s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 1.999,  sample2],
+        ["an1", 2.001,  sample2],
+        ["an1", 3.999,  sample3],
+        ["an1", 4.001,  sample3],
+        ["an1", 7.999,  sample4],
+        ["an1", 8.001,  sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-18-t-drt.html
@@ -0,0 +1,137 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-07-f.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-18-t.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.1 $" testname="animate-elem-07-t-e.svg">
+         <Paragraph>
+            Test 'calcMode'=discrete
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the height of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+             This test shows an animation with calcMode="discrete" (i.e., a jumping animation).
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-18-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="discrete"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.2;.6;.8"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="245" y="140">2</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">6</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">8</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="discrete" values="300;255;180;30" keyTimes="0;.2;.6;.8" begin="0s" dur="10s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.1 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 2s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 2s to 6s
+    assert_approx_equals(rect.width.animVal.value, 255, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 6s to 8s
+    assert_approx_equals(rect.width.animVal.value, 180, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 8s to 10s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: keyTimes="0;.2;.6;.8" begin="0s" dur="10s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 1.999,  sample1],
+        ["an1", 2.001,  sample2],
+        ["an1", 5.999,  sample2],
+        ["an1", 6.001,  sample3],
+        ["an1", 7.999,  sample3],
+        ["an1", 8.001,  sample4],
+        ["an1", 9.999,  sample4],
+        ["an1", 10.001, sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-elem-19-t-drt.html
@@ -0,0 +1,136 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>A copy of the corresponding W3C-SVG-1.1 test, which dumps the animation at certain times</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+
+<!--======================================================================-->
+<!--=  Copyright 2000 World Wide Web Consortium, (Massachusetts          =-->
+<!--=  Institute of Technology, Institut National de Recherche en        =-->
+<!--=  Informatique et en Automatique, Keio University). All Rights      =-->
+<!--=  Reserved. See http://www.w3.org/Consortium/Legal/.                =-->
+<!--======================================================================-->
+<!-- =====================================================================-->
+<!--                                                                      -->
+<!-- animation-add-BE-08.svg                                              -->
+<!-- renamed for 1.1 suite to animate-elem-07-f.svg                       -->
+<!-- renamed and split for svgt/b to animate-elem-19-t.svg              -->
+<!--                                                                      -->
+<!-- Test possible values for 'keyTimes'                                  -->
+<!--                                                                      -->
+<!-- Author : Jon Ferraiolo 11-Aug-2000                                   -->
+<!-- Revised for 1.1 : Mathias Larsson Carlander Feb/12/2002              -->
+<!-- Revised for svgt/b: Ola Andersson Jun/26/2002                        -->
+<!--                                                                      -->
+<!-- History:                                                             -->
+<!--  11-Aug-2000, JF: Serial#1 created.                                  -->
+<!--                                                                      -->
+<!-- =====================================================================-->
+<!--======================================================================-->
+<!--= Note. After October 2000, revision history is kept as CVS 'commit' =-->
+<!--= log messages, and therefore is no longer in the preceding preamble.=-->
+<!--======================================================================-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  id="svg-root" width="100%" height="100%" viewBox="0 0 480 360" version="1.1" baseProfile="tiny">
+   <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/">
+      <OperatorScript version="$Revision: 1.6 $" testname="animate-elem-19-t.svg">
+         <Paragraph>
+            Test 'calcMode'=linear
+         </Paragraph>
+         <Paragraph>
+            One animation has been defined to animate the width of a rectangle. Ruler lines and text are provided
+            to help show what the correct behavior is. The red text shows the values for the 'calcMode' and 'keyTimes' attributes. The
+            black text and ruler lines help show the size and movement of the rectangle over time.
+         </Paragraph>
+         <Paragraph>
+             This test shows an animation with calcMode="linear".
+         </Paragraph>
+      </OperatorScript>
+   </SVGTestCase>
+   <title id="test-title">animate-elem-19-t</title>
+   <desc id="test-desc">Test possible values for 'keyTimes'</desc>
+   <!--======================================================================-->
+   <!--Content of Test Case follows...                  =====================-->
+   <!--======================================================================-->
+   <g id="test-body-content">
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="45">calcMode="linear"</text>
+      <text font-family="Arial" fill="red" font-size="40"  x="3" y="100">keyTimes="0;.5;.75;1"</text>
+      <g xml:space="preserve" font-family="Arial" font-size="13.5" stroke-width="3" >
+         <g transform="translate(150,140)">
+            <text font-size="36" x="-140" y="140">Time (s):</text>
+            <text font-size="36" x="290" y="140">0</text>
+            <line x1="300" y1="0" x2="300" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="245" y="140">4</text>
+            <line x1="255" y1="0" x2="255" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="170" y="140">6</text>
+            <line x1="180" y1="0" x2="180" y2="100" fill="none" stroke="#880000" />
+            <text font-size="36" x="20" y="140">8</text>
+            <line x1="30" y1="0" x2="30" y2="100" fill="none" stroke="#880000" />
+            <rect x="0" y="0" width="300" height="80" fill="#44AAFF" stroke="#880088" stroke-width="4" >
+               <animate id="an1" attributeName="width" calcMode="linear" values="300;255;180;30" keyTimes="0;.5;.75;1" begin="0s" dur="8s" fill="freeze"/>
+            </rect>
+
+         </g>
+
+      </g>
+
+   </g>
+   <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.6 $</text>
+   <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+  <script>
+    // Pause the animation at t=0.
+    document.querySelector("svg").pauseAnimations();
+  </script>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() { // From 0s to 4s
+    assert_approx_equals(rect.width.animVal.value, 300, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample2() { // From 4s to 6s
+    assert_approx_equals(rect.width.animVal.value, 255, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample3() { // From 6s to 8s
+    assert_approx_equals(rect.width.animVal.value, 180, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+function sample4() { // From 8s to 8s
+    assert_approx_equals(rect.width.animVal.value, 30, epsilon);
+    assert_equals(rect.width.baseVal.value, 300);
+}
+
+smil_async_test((t) => {
+    rect = rootSVGElement.ownerDocument.getElementsByTagName("rect")[0];
+
+    // Sampling according to: values="300;255;180;30" keyTimes="0;.5;.75;1" begin="0s" dur="8s"
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["an1", 0.0,    sample1],
+        ["an1", 3.999,  sample2],
+        ["an1", 4.001,  sample2],
+        ["an1", 5.999,  sample3],
+        ["an1", 6.001,  sample3],
+        ["an1", 7.999,  sample4],
+        ["an1", 8.001,  sample4],
+        ["an1", 60.0,   sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-end-attribute-numeric-precision.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests end conditions are respected properly near the limits of float numeric precision</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("values", "0;300");
+animate.setAttribute("begin", "0.333333333333333s");
+animate.setAttribute("dur", "0.4256483205159505s");
+animate.setAttribute("fill", "freeze");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.x.animVal.value, 100, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    assert_approx_equals(rect.x.animVal.value, 300, epsilon);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0, sample1],
+        ["animation", 1.0, sample2]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+var animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-fill-freeze-with-repeatDur.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test for animation freeze when repeatDur is not a multiple of dur</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <rect x='0' y='0' width='50' height='50' fill='green'>
+    <animate id="anim" attributeName='x' from='0' to='100' dur='4s' begin='0s' repeatDur="6s" accumulate="sum" fill='freeze'/>
+  </rect>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect1.x.animVal.value, 0, epsilon);
+    assert_equals(rect1.x.baseVal.value, 0);
+}
+
+function sample2() {
+    assert_approx_equals(rect1.x.animVal.value, 150, epsilon);
+    assert_equals(rect1.x.baseVal.value, 0);
+}
+
+smil_async_test((t) => {
+    var rects = rootSVGElement.ownerDocument.getElementsByTagName("rect");
+    rect1 = rects[0];
+
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["anim", 0.0,   sample1],
+        ["anim", 6.0,   sample2]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-from-to-keyTimes.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests discrete from-to-keyTimes animations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "100");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("to", "200");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("keyTimes", "0;0.25");
+animate.setAttribute("calcMode", "discrete");
+animate.setAttribute("fill", "freeze");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    assert_equals(rect.x.animVal.value, 100);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+function sample2() {
+    assert_equals(rect.x.animVal.value, 200);
+    assert_equals(rect.x.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.5, sample1],
+        ["animation", 1.5, sample2],
+        ["animation", 2.5, sample2]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.clickX = 150;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-gradient-transform.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests if gradientTransform of a gradient is animateable.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var gradient = createSVGElement("linearGradient");
+gradient.setAttribute("id", "gradient");
+gradient.setAttribute("gradientUnits", "userSpaceOnUse");
+gradient.setAttribute("x1", "0");
+gradient.setAttribute("x2", "200");
+gradient.setAttribute("gradientTransform", "translate(0)");
+
+var stop1 = createSVGElement("stop");
+stop1.setAttribute("offset", "0");
+stop1.setAttribute("stop-color", "green");
+
+var stop2 = createSVGElement("stop");
+stop2.setAttribute("offset", "1");
+stop2.setAttribute("stop-color", "red");
+
+var animate = createSVGElement("animateTransform");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "gradientTransform");
+animate.setAttribute("type", "translate");
+animate.setAttribute("from", "0");
+animate.setAttribute("to", "200");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "4s");
+animate.setAttribute("fill", "freeze");
+
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("fill", "url(#gradient)");
+rect.setAttribute("width", "200");
+rect.setAttribute("height", "200");
+rect.setAttribute("onclick", "executeTest()");
+
+gradient.appendChild(stop1);
+gradient.appendChild(stop2);
+gradient.appendChild(animate);
+
+rootSVGElement.appendChild(gradient);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial conditions
+    shouldThrow("gradient.gradientTransform.animVal.consolidate()");
+    assert_equals(gradient.gradientTransform.animVal.numberOfItems, 1);
+    assert_approx_equals(gradient.gradientTransform.animVal.getItem(0).matrix.e, 0, epsilon);
+    assert_equals(gradient.gradientTransform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+
+    assert_equals(gradient.gradientTransform.baseVal.numberOfItems, 1);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).matrix.e, 0);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_equals(gradient.gradientTransform.animVal.numberOfItems, 1);
+    assert_equals(gradient.gradientTransform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+    assert_approx_equals(gradient.gradientTransform.animVal.getItem(0).matrix.e, 100, epsilon);
+
+    assert_equals(gradient.gradientTransform.baseVal.numberOfItems, 1);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).matrix.e, 0);
+}
+
+function sample3() {
+    // Check end conditions
+    assert_equals(gradient.gradientTransform.animVal.numberOfItems, 1);
+    assert_equals(gradient.gradientTransform.animVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+    assert_approx_equals(gradient.gradientTransform.animVal.getItem(0).matrix.e, 200, epsilon);
+
+    assert_equals(gradient.gradientTransform.baseVal.numberOfItems, 1);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).type, SVGTransform.SVG_TRANSFORM_TRANSLATE);
+    assert_equals(gradient.gradientTransform.baseVal.getItem(0).matrix.e, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0, sample1],
+        ["animation", 2.0, sample2],
+        ["animation", 3.999, sample3],
+        ["animation", 4.001, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-inherit-css-property.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Tests animation with 'inherit'.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var g = createSVGElement("g");
+g.setAttribute("fill", "green");
+
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("width", "100px");
+rect.setAttribute("height", "100px");
+rect.setAttribute("fill", "red");
+rect.setAttribute("onclick", "executeTest()");
+g.appendChild(rect);
+
+var animateInherit = createSVGElement("animate");
+animateInherit.setAttribute("id", "animateInherit");
+animateInherit.setAttribute("attributeName", "fill");
+animateInherit.setAttribute("from", "red");
+animateInherit.setAttribute("to", "inherit");
+animateInherit.setAttribute("dur", "3s");
+animateInherit.setAttribute("begin", "0s");
+animateInherit.setAttribute("fill", "freeze");
+rect.appendChild(animateInherit);
+rootSVGElement.appendChild(g);
+
+// Setup animation test
+function sample1() {
+    // Check initial conditions
+    expectFillColor(rect, 255, 0, 0);
+    assert_equals(rect.style.fill, "");
+}
+
+function sample2() {
+    // Check half-time conditions
+    expectFillColor(rect, 128, 64, 0);
+    assert_equals(rect.style.fill, "");
+}
+
+function sample3() {
+    // Check end conditions
+    expectFillColor(rect, 0, 128, 0);
+    assert_equals(rect.style.fill, "");
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animateInherit", 0.0, sample1],
+        ["animateInherit", 1.5, sample2],
+        ["animateInherit", 3.0, sample3]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-insert-begin.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test behavior of dynamically inserting animate with begin attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "0");
+rect.setAttribute("y", "45");
+rect.setAttribute("width", "10");
+rect.setAttribute("height", "10");
+rect.setAttribute("fill", "green");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("begin", "0");
+animate.setAttribute("from", "0");
+animate.setAttribute("to", "90");
+animate.setAttribute("dur", "3s");
+animate.setAttribute("fill", "freeze");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 0);
+}
+
+function sample2() {
+    assert_approx_equals(rect.x.animVal.value, 90, epsilon);
+    assert_equals(rect.x.baseVal.value, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0, sample1],
+        ["animation", 3.0, sample2],
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-insert-no-begin.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Test behavior of dynamically inserting animate without begin attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("x", "0");
+rect.setAttribute("y", "45");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("fill", "green");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "x");
+animate.setAttribute("from", "0");
+animate.setAttribute("to", "90");
+animate.setAttribute("dur", "3s");
+animate.setAttribute("fill", "freeze");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(rect.x.animVal.value, 0, epsilon);
+    assert_equals(rect.x.baseVal.value, 0);
+}
+
+function sample2() {
+    assert_approx_equals(rect.x.animVal.value, 90, epsilon);
+    assert_equals(rect.x.baseVal.value, 0);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0, sample1],
+        ["animation", 3.0, sample2]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+window.animationStartsImmediately = true;
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-keySplines.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Testing correct parsing of keySplines.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+var rect = createSVGElement("rect");
+rect.setAttribute("id", "rect");
+rect.setAttribute("fill", "green");
+rect.setAttribute("x", "0");
+rect.setAttribute("y", "0");
+rect.setAttribute("width", "100");
+rect.setAttribute("height", "100");
+rect.setAttribute("onclick", "executeTest()");
+
+var animate = createSVGElement("animate");
+animate.setAttribute("id", "animation");
+animate.setAttribute("attributeName", "height");
+animate.setAttribute("calcMode", "spline");
+animate.setAttribute("keyTimes", " 0 ; 0.3333333 ; 0.666666; 1 ");
+animate.setAttribute("keySplines", "  0 ,0  1 , 1  ;   0 0 , 1 ,    1  ;  .75 , 0 , 0 , .75 ; ");
+animate.setAttribute("values", "200;167;111;0");
+animate.setAttribute("begin", "0s");
+animate.setAttribute("dur", "9s");
+rect.appendChild(animate);
+rootSVGElement.appendChild(rect);
+
+// Setup animation test
+function sample1() {
+    // Check initial/end conditions
+    assert_approx_equals(rect.height.animVal.value, 167, epsilon);
+    assert_equals(rect.height.baseVal.value, 100);
+}
+
+function sample2() {
+    // Check half-time conditions
+    assert_approx_equals(rect.height.animVal.value, 111, epsilon);
+    assert_equals(rect.height.baseVal.value, 100);
+}
+
+function sample3() {
+    // Check just before-end conditions
+    assert_approx_equals(rect.height.animVal.value, 0, epsilon);
+    assert_equals(rect.height.baseVal.value, 100);
+}
+
+function sample4() {
+    // Check end conditions
+    assert_approx_equals(rect.height.animVal.value, 100, epsilon);
+    assert_equals(rect.height.baseVal.value, 100);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 3.0,   sample1],
+        ["animation", 6.0,   sample2],
+        ["animation", 8.999, sample3],
+        ["animation", 9.001, sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/animate-marker-orient-from-angle-to-angle.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Animate SVGMarkerElement orientAttr from an angle to an angle</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+
+<svg>
+</svg>
+
+<script>
+var rootSVGElement = document.querySelector("svg");
+var epsilon = 1.0;
+
+// Setup test document
+
+var marker = createSVGElement("marker");
+marker.setAttribute("id", "marker");
+marker.setAttribute("viewBox", "0 0 10 10");
+marker.setAttribute("markerWidth", "2");
+marker.setAttribute("markerHeight", "2");
+marker.setAttribute("refX", "5");
+marker.setAttribute("refY", "5");
+marker.setAttribute("markerUnits", "strokeWidth");
+
+var markerPath = createSVGElement("path");
+markerPath.setAttribute("fill", "blue");
+markerPath.setAttribute("d", "M 5 0 L 10 10 L 0 10 Z");
+marker.appendChild(markerPath);
+
+var defsElement = createSVGElement("defs");
+defsElement.appendChild(marker);
+rootSVGElement.appendChild(defsElement);
+
+var path = createSVGElement("path");
+path.setAttribute("id", "path");
+path.setAttribute("onclick", "executeTest()");
+path.setAttribute("fill", "none");
+path.setAttribute("stroke", "green");
+path.setAttribute("stroke-width", "10");
+path.setAttribute("marker-start", "url(#marker)");
+path.setAttribute("marker-end", "url(#marker)");
+path.setAttribute("d", "M 130 135 L 180 135 L 180 185");
+path.setAttribute("transform", "translate(-130, -120)");
+rootSVGElement.appendChild(path);
+
+var animate1 = createSVGElement("animate");
+animate1.setAttribute("id", "animation");
+animate1.setAttribute("attributeName", "orient");
+animate1.setAttribute("begin", "0s");
+animate1.setAttribute("dur", "4s");
+animate1.setAttribute("from", "90deg");
+animate1.setAttribute("to", "180deg");
+animate1.setAttribute("fill", "freeze");
+marker.appendChild(animate1);
+
+// Setup animation test
+function sample1() {
+    assert_approx_equals(marker.orientAngle.animVal.value, 0, epsilon);
+    assert_equals(marker.orientAngle.baseVal.value, 0);
+
+    assert_equals(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+    assert_equals(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+}
+
+function sample2() {
+    assert_approx_equals(marker.orientAngle.animVal.value, 90, epsilon);
+    assert_equals(marker.orientAngle.baseVal.value, 0);
+
+    assert_equals(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+    assert_equals(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+}
+
+function sample3() {
+    assert_approx_equals(marker.orientAngle.animVal.value, 135, epsilon);
+    assert_equals(marker.orientAngle.baseVal.value, 0);
+
+    assert_equals(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+    assert_equals(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+}
+
+function sample4() {
+    assert_approx_equals(marker.orientAngle.animVal.value, 180, epsilon);
+    assert_equals(marker.orientAngle.baseVal.value, 0);
+
+    assert_equals(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+    assert_equals(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE);
+}
+
+smil_async_test((t) => {
+    const expectedValues = [
+        // [animationId, time, sampleCallback]
+        ["animation", 0.0,   sample1],
+        ["animation", 0.001, sample2],
+        ["animation", 2.0,   sample3],
+        ["animation", 3.999, sample4],
+        ["animation", 4.001, sample4]
+    ];
+
+    runAnimationTest(t, expectedValues);
+});
+
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/animations/scripted/paced-value-animation-overwrites-keyTimes.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Paced value animation doesn't overwrite keyTimes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta charset="utf-8">
+<link rel="author" title="Edvard Thörnros" href="mailto:edvardt@opera.com">
+<link rel="help" href="https://www.w3.org/TR/SMIL20/animation.html#animationNS-animateMotionElement">
+<link rel="bug" href="https://bugs.chromium.org/p/chromium/issues/detail?id=231525&hotlist_id=5524&sort=%20rank%20-ID">
+
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+  <rect x="151" y="1" width="98" height="98" fill="red"/>
+  <rect id="rect" x="0" y="0" width="100" height="100" fill="green">
+    <animate id="animate1" attributeName="x" dur="10s" calcMode="paced" values="100; 150; 200;" keyTimes="0; 0.2; 1"/>
+  </rect>
+</svg>
+
+<script>
+  async_test(function(t) {
+    window.onload = t.step_func(function() {
+      let svg = document.getElementById('svg');
+      let animate1 = document.getElementById('animate1');
+      let rect = document.getElementById('rect');
+      t.step_timeout(function() {
+        // animate1's keyTimes should not be affected by starting with calcMode=paced
+        animate1.setAttribute('calcMode', 'linear');
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+        window.requestAnimationFrame(t.step_func(function() {
+          window.requestAnimationFrame(t.step_func_done(function() {
+            assert_approx_equals(rect.x.animVal.value, 150, 5);
+          }));
+        }));
+      }, 10);
+    });
+  });
+</script>
--- a/testing/web-platform/tests/user-timing/mark-measure-return-objects.any.js
+++ b/testing/web-platform/tests/user-timing/mark-measure-return-objects.any.js
@@ -3,17 +3,17 @@ async_test(function (t) {
   const measure = self.performance.measure("measure1");
   assert_true(measure instanceof PerformanceMeasure);
   t.done();
 }, "L3: performance.measure(name) should return an entry.");
 
 async_test(function (t) {
   self.performance.clearMeasures();
   const measure = self.performance.measure("measure2",
-      { startTime: 12, endTime:23 });
+      { start: 12, end: 23 });
   assert_true(measure instanceof PerformanceMeasure);
   t.done();
 }, "L3: performance.measure(name, param1) should return an entry.");
 
 async_test(function (t) {
   self.performance.clearMeasures();
   self.performance.mark("1");
   self.performance.mark("2");
--- a/testing/web-platform/tests/user-timing/measure-with-dict.any.js
+++ b/testing/web-platform/tests/user-timing/measure-with-dict.any.js
@@ -25,20 +25,22 @@ async_test(function (t) {
         { entryType: "measure", name: "measure10", detail: null, startTime: timeStamp1 },
         { entryType: "measure", name: "measure11", detail: null, startTime: timeStamp3 },
         { entryType: "measure", name: "measure12", detail: null, startTime: 0 },
         { entryType: "measure", name: "measure13", detail: null, startTime: 0 },
         { entryType: "measure", name: "measure14", detail: null, startTime: timeStamp3, duration: timeStamp1 - timeStamp3 },
         { entryType: "measure", name: "measure15", detail: null, startTime: timeStamp1, duration: timeStamp2 - timeStamp1 },
         { entryType: "measure", name: "measure16", detail: null, startTime: timeStamp1 },
         { entryType: "measure", name: "measure17", detail: { customInfo: 159 }, startTime: timeStamp3, duration: timeStamp2 - timeStamp3 },
-        { entryType: "measure", name: "measure18", detail: null, startTime: 0 },
-        { entryType: "measure", name: "measure19", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure18", detail: null, startTime: timeStamp1, duration: timeStamp2 - timeStamp1 },
+        { entryType: "measure", name: "measure19", detail: null, startTime: timeStamp1, duration: timeStamp2 - timeStamp1 },
         { entryType: "measure", name: "measure20", detail: null, startTime: 0 },
-        { entryType: "measure", name: "measure21", detail: null, startTime: 0 }];
+        { entryType: "measure", name: "measure21", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure22", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure23", detail: null, startTime: 0 }];
     const observer = new PerformanceObserver(
         t.step_func(function (entryList, obs) {
           measureEntries =
             measureEntries.concat(entryList.getEntries());
           if (measureEntries.length >= expectedEntries.length) {
             checkEntries(measureEntries, expectedEntries);
             observer.disconnect();
             t.done();
@@ -75,21 +77,25 @@ async_test(function (t) {
     returnedEntries.push(
         self.performance.measure("measure14", { start: timeStamp3, end: 'mark1' }));
     returnedEntries.push(
         self.performance.measure("measure15", { start: timeStamp1, end: timeStamp2, detail: undefined }));
     returnedEntries.push(
         self.performance.measure("measure16", { start: 'mark1', end: undefined, detail: null }));
     returnedEntries.push(
         self.performance.measure("measure17", { start: timeStamp3, end: 'mark2', detail: { customInfo: 159 }}));
+    returnedEntries.push(
+        self.performance.measure("measure18", { start: timeStamp1, duration: timeStamp2 - timeStamp1 }));
+    returnedEntries.push(
+        self.performance.measure("measure19", { duration: timeStamp2 - timeStamp1, end: timeStamp2 }));
     // {}, null, undefined, invalid-dict passed to startOrOptions are interpreted as start time being 0.
-    returnedEntries.push(self.performance.measure("measure18", {}, 'mark1'));
-    returnedEntries.push(self.performance.measure("measure19", null, 'mark1'));
-    returnedEntries.push(self.performance.measure("measure20", undefined, 'mark1'));
-    returnedEntries.push(self.performance.measure("measure21", { invalidDict:1 }, 'mark1'));
+    returnedEntries.push(self.performance.measure("measure20", {}, 'mark1'));
+    returnedEntries.push(self.performance.measure("measure21", null, 'mark1'));
+    returnedEntries.push(self.performance.measure("measure22", undefined, 'mark1'));
+    returnedEntries.push(self.performance.measure("measure23", { invalidDict:1 }, 'mark1'));
     checkEntries(returnedEntries, expectedEntries);
 }, "measure entries' detail and start/end are customizable");
 
 test(function() {
     this.add_cleanup(cleanupPerformanceTimeline);
     assert_throws(new TypeError(), function() {
       self.performance.measure("optionsAndNumberEnd", {'start': 2}, 12);
     }, "measure should throw a TypeError when passed an options object and an end time");
--- a/testing/web-platform/tests/user-timing/measure_exception.html
+++ b/testing/web-platform/tests/user-timing/measure_exception.html
@@ -22,11 +22,13 @@ performance.mark('ExistMark');
 test_method_throw_exception('performance.measure()', TypeError());
 test_method_throw_exception('performance.measure("Exception1", "NonExistMark1")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception2", "NonExistMark1", "navigationStart")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception3", "navigationStart", "NonExistMark1")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception4", "NonExistMark1", "ExistMark")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception5", "ExistMark", "NonExistMark1")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception6", "NonExistMark1", "NonExistMark2")', 'SYNTAX_ERR');
 test_method_throw_exception('performance.measure("Exception7", "redirectStart")', 'INVALID_ACCESS_ERR');
+test_method_throw_exception('performance.measure("Exception8", {"detail": "non-empty"})', TypeError());
+test_method_throw_exception('performance.measure("Exception9", {"start": 1, "duration": 2, "end": 3})', TypeError());
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/user-timing/structured-serialize-detail.any.js
+++ b/testing/web-platform/tests/user-timing/structured-serialize-detail.any.js
@@ -25,38 +25,38 @@ test(function() {
   assert_throws("DataCloneError", ()=>{
     new PerformanceMark("A", { detail });
   }, "Trying to structured-serialize a Symbol.");
 }, "Mark: Throw an exception when the detail property cannot be structured-serialized.");
 
 test(function() {
   performance.clearMeasures();
   const detail = { randomInfo: 123 }
-  const measureEntry = performance.measure("A", { detail });
+  const measureEntry = performance.measure("A", { start: 0, detail });
   assert_not_equals(measureEntry.detail, detail);
 }, "The detail property in the measure method should be structured-clone.");
 
 test(function() {
   performance.clearMeasures();
   const detail = { randomInfo: 123 }
-  const measureEntry = performance.measure("A", { detail });
+  const measureEntry = performance.measure("A", { start: 0, detail });
   assert_equals(measureEntry.detail, measureEntry.detail);
 }, "The detail property in the measure method should be the same reference.");
 
 test(function() {
   performance.clearMeasures();
   const measureEntry = performance.measure("A");
   assert_equals(measureEntry.detail, null);
 }, "When accessing detail from a measure entry and the detail is not provided, just return a null value.");
 
 test(function() {
   performance.clearMeasures();
   const detail = { unserializable: Symbol() };
   assert_throws("DataCloneError", ()=>{
-    performance.measure("A", { detail });
+    performance.measure("A", { start: 0, detail });
   }, "Trying to structured-serialize a Symbol.");
 }, "Measure: Throw an exception when the detail property cannot be structured-serialized.");
 
 test(function() {
   const bar = { 1: 2 };
   const detail = { foo: 1, bar };
   const mark = performance.mark("m", { detail });
   detail.foo = 2;
--- a/testing/web-platform/tests/web-nfc/NDEFMessage_constructor.https.html
+++ b/testing/web-platform/tests/web-nfc/NDEFMessage_constructor.https.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <title>NDEFMessage constructor</title>
 <link rel="help" href="https://w3c.github.io/web-nfc/#dom-ndefmessage">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
+<script src="resources/nfc-helpers.js"></script>
 <script>
 
   test(() => {
     assert_equals(NDEFMessage.length, 1);
     assert_throws(new TypeError, () => new NDEFMessage());
   }, 'NDEFMessage constructor without init dict');
 
   test(() => {
--- a/testing/web-platform/tests/web-nfc/NDEFRecord_constructor.https.html
+++ b/testing/web-platform/tests/web-nfc/NDEFRecord_constructor.https.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <title>NDEFRecord constructor</title>
 <link rel="help" href="https://w3c.github.io/web-nfc/#dom-ndefrecord">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
+<script src="resources/nfc-helpers.js"></script>
 <script>
 
   test(() => {
     assert_equals(NDEFRecord.length, 1);
     assert_throws(new TypeError, () => new NDEFRecord());
   }, 'NDEFRecord constructor without init dict');
 
   test(() => {
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader-manual.https.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC: NFCReader tests</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-promise_test(async t => {
-  const reader = new NFCReader();
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  assert_true(event instanceof NFCReadingEvent);
-}, 'Test that nfc watch success if NFC HW is enabled.');
-
-promise_test(async t => {
-  const reader = new NFCReader({url: "https://a.com"});
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  assert_true(event instanceof NFCReadingEvent);
-}, 'Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL.');
-
-promise_test(async t => {
-  const reader = new NFCReader({url: "https://a.com/*"});
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  assert_true(event instanceof NFCReadingEvent);
-}, 'Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL with "*"' +
-   ' wildcard character in path.');
-
-promise_test(async t => {
-  const reader = new NFCReader({url: "https://a.com/*/bar"});
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  assert_true(event instanceof NFCReadingEvent);
-}, 'Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL with "*"' +
-   ' wildcard character in the beginning of path component followed by' +
-   ' subpath.');
-
-promise_test(async t => {
-  const reader = new NFCReader({url: ""});
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  assert_true(event instanceof NFCReadingEvent);
-}, 'Test that NFCReader.start succeeds if NFCReaderOptions.url is empty.');
-
-</script>
--- a/testing/web-platform/tests/web-nfc/NFCReader.https.html
+++ b/testing/web-platform/tests/web-nfc/NFCReader.https.html
@@ -1,40 +1,131 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web NFC: NFCReader tests</title>
 <link rel="author" title="Intel" href="http://www.intel.com"/>
 <link rel="help" href="https://w3c.github.io/web-nfc/"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-
-<div id="log"></div>
-
+<script src="resources/nfc-helpers.js"></script>
 <script>
 
 "use strict";
 
 function waitSyntaxErrorPromise(t, reader) {
   const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
   const promise = readerWatcher.wait_for("error").then(event => {
     assert_equals(event.error.name, 'SyntaxError');
   });
-  // NFCReader#start() synchronously dispatches the syntax error event.
+  // NFCReader#start() asynchronously dispatches the syntax error event.
   reader.start();
   return promise;
 }
 
-promise_test(t => {
+promise_test(async t => {
   const reader = new NFCReader({url: "www.a.com"});
-  return waitSyntaxErrorPromise(t, reader);
-}, 'Test that NFCReader.start fails if NFCReaderOptions.url is missing components.');
+  await waitSyntaxErrorPromise(t, reader);
+}, "Test that NFCReader.start fails if NFCReaderOptions.url is missing \
+components.");
 
-promise_test(t => {
+promise_test(async t => {
   const reader = new NFCReader({url: "invalid"});
-  return waitSyntaxErrorPromise(t, reader);
-}, 'Test that NFCReader.start fails if NFCReaderOptions.url is invalid.');
+  await waitSyntaxErrorPromise(t, reader);
+}, "Test that NFCReader.start fails if NFCReaderOptions.url is invalid.");
+
+promise_test(async t => {
+  const reader = new NFCReader({url: "http://a.com"});
+  await waitSyntaxErrorPromise(t, reader);
+}, "Test that NFCReader.start fails if NFCReaderOptions.url has wrong \
+protocol.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader();
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+  reader.start();
+  mockNFC.setHWStatus(NFCHWStatus.DISABLED);
+  const event = await readerWatcher.wait_for("error");
+  assert_equals(event.error.name, 'NotReadableError');
+}, "NFCReader.start should fail if NFC HW is disabled.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader();
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+  reader.start();
+  mockNFC.setHWStatus(NFCHWStatus.NOT_SUPPORTED);
+  const event = await readerWatcher.wait_for("error");
+  assert_equals(event.error.name, 'NotSupportedError');
+}, "NFCReader.start should fail if NFC HW is not supported.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader();
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_true(event instanceof NFCReadingEvent);
+    reader.stop();
+  });
+  // NFCReader#start() asynchronously dispatches the reading event.
+  reader.start();
+  await promise;
+}, "Test that nfc watch success if NFC HW is enabled.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader({url: "https://a.com"});
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
 
-promise_test(t => {
-  const reader = new NFCReader({url: "http://a.com"});
-  return waitSyntaxErrorPromise(t, reader);
-}, 'Test that NFCReader.start fails if NFCReaderOptions.url has wrong protocol.');
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_true(event instanceof NFCReadingEvent);
+    reader.stop();
+  });
+  // NFCReader#start() asynchronously dispatches the reading event.
+  reader.start();
+  await promise;
+}, "Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader({url: "https://a.com/*"});
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_true(event instanceof NFCReadingEvent);
+    reader.stop();
+  });
+  // NFCReader#start() asynchronously dispatches the reading event.
+  reader.start();
+  await promise;
+}, "Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL \
+with '*' wildcard character in path.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader({url: "https://a.com/*/bar"});
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_true(event instanceof NFCReadingEvent);
+    reader.stop();
+  });
+  // NFCReader#start() asynchronously dispatches the reading event.
+  reader.start();
+  await promise;
+}, "Test that NFCReader.start succeeds if NFCReaderOptions.url is valid URL \
+with '*' wildcard character in the beginning of path component followed by \
+subpath.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NFCReader({url: ""});
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_true(event instanceof NFCReadingEvent);
+    reader.stop();
+  });
+  // NFCReader#start() asynchronously dispatches the reading event.
+  reader.start();
+  await promise;
+}, "Test that NFCReader.start succeeds if NFCReaderOptions.url is empty.");
 
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-nfc/NFCReader_options.https.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Web NFC: NFCReader option tests</title>
+<link rel="author" title="Intel" href="http://www.intel.com"/>
+<link rel="help" href="https://w3c.github.io/web-nfc/"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/nfc-helpers.js"></script>
+<script>
+
+'use strict';
+
+const NFCReaderOptionTests =
+  [
+    {
+      desc: "Test that reading data succeed when NFCReaderOptions'" +
+            " recordType is set to 'empty'.",
+      readOptions: {recordType: "empty"},
+      unmatchedReadOptions: {recordType: "json"},
+      message: createMessage([createRecord('empty', '')])
+    },
+    {
+      desc: "Test that reading data succeed when NFCReaderOptions'" +
+            " recordType is set to 'json'.",
+      readOptions: {recordType: "json"},
+      unmatchedReadOptions: {recordType: "url"},
+      message: createMessage([createJsonRecord(test_json_data)])
+    },
+    {
+      desc: "Test that reading data succeed when NFCReaderOptions'" +
+            " recordType is set to 'opaque'.",
+      readOptions: {recordType: "opaque"},
+      unmatchedReadOptions: {recordType: "json"},
+      message: createMessage([createOpaqueRecord(test_buffer_data)])
+    },
+    {
+      desc: "Test that reading data succeed when NFCReaderOptions'" +
+            " recordType is set to 'text'.",
+      readOptions: {recordType: "text"},
+      unmatchedReadOptions: {recordType: "json"},
+      message: createMessage([createTextRecord(test_text_data)])
+    },
+    {
+      desc: "Test that reading data succeed when NFCReaderOptions'" +
+            " recordType is set to 'url'.",
+      readOptions: {recordType: "url"},
+      unmatchedReadOptions: {recordType: "json"},
+      message: createMessage([createUrlRecord(test_url_data)])
+    },
+    {
+      desc: "Test that the url of NFCReaderOptions filters relevant data" +
+            " sources correctly.",
+      readOptions: {url: `${location.origin}/custom/path`},
+      unmatchedReadOptions: {url: `${location.origin}/custom/invalid`},
+      message: {url: `${location.origin}/custom/path/update`,
+          records: [createTextRecord(test_text_data)]}
+    },
+    {
+      desc: "Test that the mediaType of NFCReaderOptions filters relevant data" +
+            " sources correctly.",
+      readOptions: {mediaType: "application/octet-stream"},
+      unmatchedReadOptions: {mediaType: "application/json"},
+      message: createMessage([createOpaqueRecord(test_buffer_data)])
+    },
+    {
+      desc: "Test that the compatibility of NFCReaderOptions filters relevant data" +
+            " sources correctly.",
+      readOptions: {compatibility: "vendor"},
+      unmatchedReadOptions: {compatibility: "nfc-forum"},
+      message: createMessage([createTextRecord(test_text_data)]),
+    }
+  ];
+
+const ReadMultiMessagesTests =
+  [
+    {
+      desc: "Test that filtering 'empty' record from different messages" +
+            " correctly with NFCReaderOptions' recordType is set to 'empty'.",
+      readOptions: {recordType: "empty"},
+      message: createMessage([createRecord('empty', '')]),
+      unmatchedMessage: createMessage([createJsonRecord(test_json_data)]),
+    },
+    {
+      desc: "Test that filtering 'json' record from different messages" +
+            " correctly with NFCReaderOptions' recordType is set to 'json'.",
+      readOptions: {recordType: "json"},
+      message: createMessage([createJsonRecord(test_json_data)]),
+      unmatchedMessage: createMessage([createUrlRecord(test_url_data)])
+    },
+    {
+      desc: "Test that filtering 'opaque' record from different messages" +
+            " correctly with NFCReaderOptions' recordType is set to 'opaque'.",
+      readOptions: {recordType: "opaque"},
+      message: createMessage([createOpaqueRecord(test_buffer_data)]),
+      unmatchedMessage: createMessage([createJsonRecord(test_json_data)])
+    },
+    {
+      desc: "Test that filtering 'text' record from different messages" +
+            " correctly with NFCReaderOptions' recordType is set to 'text'.",
+      readOptions: {recordType: "text"},
+      message: createMessage([createTextRecord(test_text_data)]),
+      unmatchedMessage: createMessage([createUrlRecord(test_url_data)])
+    },
+    {
+      desc: "Test that filtering 'url' record from different messages" +
+            " correctly with NFCReaderOptions' recordType is set to 'url'.",
+      readOptions: {recordType: "url"},
+      message: createMessage([createUrlRecord(test_url_data)]),
+      unmatchedMessage: createMessage([createTextRecord(test_text_data)])
+    },
+    {
+      desc: "Test that filtering 'text' record from different messages" +
+            " correctly with NFCReaderOptions' url set.",
+      readOptions: {url: `${location.origin}/custom/path`},
+      message: {url: `${location.origin}/custom/path/update`,
+          records: [createTextRecord(test_text_data)]},
+      unmatchedMessage: {url: `${location.origin}/custom/invalid`,
+          records: [createUrlRecord(test_url_data)]}
+    },
+    {
+      desc: "Test that filtering 'opaque' record from different messages" +
+            " correctly with NFCReaderOptions' mediaType set.",
+      readOptions: {mediaType: "application/octet-stream"},
+      message: createMessage([createOpaqueRecord(test_buffer_data)]),
+      unmatchedMessage: createMessage([createJsonRecord(test_json_data)])
+    },
+    {
+      desc: "Test that filtering 'text' record from different messages" +
+            " correctly with NFCReaderOptions' compatibility set.",
+      readOptions: {compatibility: "nfc-forum"},
+      message: createMessage([createTextRecord(test_text_data)]),
+      unmatchedMessage: createMessage([createJsonRecord(test_json_data)]),
+      unmatchedCompatibility: "vendor"
+    }
+  ];
+
+for (let NFCReaderOptionTest of NFCReaderOptionTests) {
+  testNFCReaderOptions(
+    NFCReaderOptionTest.message,
+    NFCReaderOptionTest.readOptions,
+    NFCReaderOptionTest.unmatchedReadOptions,
+    NFCReaderOptionTest.desc
+  );
+}
+
+for (let readMultiMessagesTest of ReadMultiMessagesTests) {
+  // Sets default message's associated compatibility
+  let unmatchedCompatibility = "nfc-forum";
+  if(readMultiMessagesTest.unmatchedCompatibility)
+    unmatchedCompatibility = readMultiMessagesTest.unmatchedCompatibility;
+
+  testReadingMultiMessages(
+    readMultiMessagesTest.message,
+    readMultiMessagesTest.readOptions,
+    readMultiMessagesTest.unmatchedMessage,
+    unmatchedCompatibility,
+    readMultiMessagesTest.desc
+  );
+}
+</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_mediaType-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: NFCReader NFCReaderOptions mediaType test</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that the mediaType of NFCReaderOptions filters relevant data sources correctly.";
-const readOptions = {mediaType: "application/octet-stream"};
-const unacceptableReadOptions = {mediaType: "application/json"};
-const message = createMessage([createOpaqueRecord(test_buffer_data)]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_recordType_empty-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: write and read empty records</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that write and read data succeed when NFCReaderOptions's recordType is set to 'empty'.";
-const readOptions = {recordType: "empty"};
-const unacceptableReadOptions = {recordType: "json"};
-const message = createMessage([createRecord('empty', '')]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_recordType_json-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: write and read json records</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that write and read data succeed when NFCReaderOptions's recordType is set to 'json'.";
-const readOptions = {recordType: "json"};
-const unacceptableReadOptions = {recordType: "url"};
-const message = createMessage([createJsonRecord(test_json_data)]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_recordType_opaque-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: write and read opaque records</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that write and read data succeed when NFCReaderOptions's recordType is set to 'opaque'.";
-const readOptions = {recordType: "opaque"};
-const unacceptableReadOptions = {recordType: "json"};
-const message = createMessage([createOpaqueRecord(test_buffer_data)]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_recordType_text-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: write and read text records</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that write and read data succeed when NFCReaderOptions's recordType is set to 'text'.";
-const readOptions = {recordType: "text"};
-const unacceptableReadOptions = {recordType: "json"};
-const message = createMessage([createTextRecord(test_text_data)]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_recordType_url-manual.https.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: write and read url records</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that write and read data succeed when NFCReaderOptions's recordType is set to 'url'.";
-const readOptions = {recordType: "url"};
-const unacceptableReadOptions = {recordType: "json"};
-const message = createMessage([createUrlRecord(test_url_data)]);
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCReader_options_url-manual.https.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: NFCReader NFCReaderOptions url test</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-const desc = "Test that the url of NFCReaderOptions filters relevant data sources correctly.";
-const readOptions = {url: `${location.origin}/custom/path`};
-const unacceptableReadOptions = {url: `${location.origin}/custom/invalid`};
-const message = {
-  url: "/custom/path/update",
-  records: [createTextRecord(test_text_data)]
-}
-
-testNFCReaderOptions(message, readOptions, unacceptableReadOptions, desc);
-
-</script>
--- a/testing/web-platform/tests/web-nfc/NFCReadingEvent_constructor.https.html
+++ b/testing/web-platform/tests/web-nfc/NFCReadingEvent_constructor.https.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <title>NFCReadingEvent constructor</title>
 <link rel="help" href="https://w3c.github.io/web-nfc/#dom-nfcreadingevent">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
+<script src="resources/nfc-helpers.js"></script>
 <script>
 
   test(() => {
     assert_equals(NFCReadingEvent.length, 2);
     assert_throws(new TypeError, () => new NFCReadingEvent('message'));
   }, 'NFCReadingEvent constructor without init dict');
 
   test(() => {
@@ -29,12 +29,12 @@
     assert_equals(0, event.message.url.length, 'empty url');
   }, 'NFCReadingEvent constructor with null message');
 
   test(() => {
     const message = createMessage([createJsonRecord(test_json_data)]);
     const event = new NFCReadingEvent('type', {serialNumber: '', message: message});
     assert_equals(event.type, 'type', 'type');
     assert_equals(event.serialNumber, '', 'serialNumber');
-    assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message), 'message');
+    assertWebNDEFMessagesEqual(event.message, message);
   }, 'NFCReadingEvent constructor with valid parameters');
 
 </script>
--- a/testing/web-platform/tests/web-nfc/NFCWriter_push.https.html
+++ b/testing/web-platform/tests/web-nfc/NFCWriter_push.https.html
@@ -1,19 +1,16 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web NFC: Test exceptions in NFCWriter.push</title>
 <link rel="author" title="Intel" href="http://www.intel.com"/>
 <link rel="help" href="https://w3c.github.io/web-nfc/"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-
-<div id="log"></div>
-
+<script src="resources/nfc-helpers.js"></script>
 <script>
 
 "use strict";
 
 const invalid_type_messages =
     [
       // Invalid NDEFMessageSource type
       undefined,
@@ -79,115 +76,149 @@ const invalid_signals = [
   123,
   {},
   true,
   Symbol(),
   () => {},
   self
 ];
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const promises = [];
   invalid_type_messages.forEach(message => {
     promises.push(
       promise_rejects(t, new TypeError(), writer.push(message)));
   });
-  return Promise.all(promises);
+  await Promise.all(promises);
 }, "Test that promise is rejected with TypeError if NDEFMessageSource is invalid.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const promises = [];
   invalid_syntax_messages.forEach(message => {
     promises.push(
       promise_rejects(t, 'SyntaxError', writer.push(message)));
   });
-  return Promise.all(promises);
-}, "'Test that promise is rejected with SyntaxError if NDEFMessageSource contains invalid records.");
+  await Promise.all(promises);
+}, "Test that promise is rejected with SyntaxError if NDEFMessageSource contains\
+ invalid records.");
 
-promise_test(async t => {
+nfc_test(async (t, mockNFC) => {
   const writer = new NFCWriter();
   const controller = new AbortController();
-  const p = writer.push(test_text_data, { signal: controller.signal });
+
+  //Make sure push is pending
+  mockNFC.setPendingPushCompleted(false);
+  const p = writer.push(test_text_data,
+      { signal: controller.signal, timeout: 100 });
   const rejected = promise_rejects(t, 'AbortError', p);
   let callback_called = false;
-  t.step_timeout(() => {
-    callback_called = true;
-    controller.abort();
-  }, 10);
+  await new Promise(resolve => {
+    t.step_timeout(() => {
+      callback_called = true;
+      controller.abort();
+      resolve();
+    }, 10);
+  });
   await rejected;
   assert_true(callback_called, 'timeout should have caused the abort');
 }, "NFCWriter.push should fail if abort push request before push happends.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const controller = new AbortController();
   assert_false(controller.signal.aborted);
   controller.abort();
   assert_true(controller.signal.aborted);
-  return promise_rejects(t, 'AbortError', writer.push(test_text_data, { signal: controller.signal }));
+  await promise_rejects(t, 'AbortError',
+      writer.push(test_text_data, { signal: controller.signal }));
 }, "NFCWriter.push should fail if signal's aborted flag is set.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const promises = [];
   invalid_signals.forEach(invalid_signal => {
-    promises.push(
-      promise_rejects(t, new TypeError(), writer.push(test_text_data, { signal: invalid_signal })));
+    promises.push(promise_rejects(t, new TypeError(),
+        writer.push(test_text_data, { signal: invalid_signal })));
   });
-  return Promise.all(promises);
+  await Promise.all(promises);
 }, "NFCWriter.push should fail if signal is not an AbortSignal.");
 
-promise_test(t => {
+nfc_test(async (t, mockNFC) => {
   const writer = new NFCWriter();
-  return promise_rejects(t, new TypeError(), writer.push(test_text_data, { timeout: "invalid"}));
-}, "NFCWriter.push should fail with TypeError when invalid timeout is provided.");
+  mockNFC.setHWStatus(NFCHWStatus.DISABLED);
+  await promise_rejects(t, 'NotReadableError', writer.push(test_text_data));
+}, "NFCWriter.push should fail when NFC HW is disabled.");
 
-promise_test(t => {
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  mockNFC.setHWStatus(NFCHWStatus.NOT_SUPPORTED);
+  await promise_rejects(t, 'NotSupportedError', writer.push(test_text_data));
+}, "NFCWriter.push should fail when NFC HW is not supported.");
+
+promise_test(async t => {
   const writer = new NFCWriter();
-  return promise_rejects(t, new TypeError(), writer.push(test_text_data, { timeout: -1 }));
-}, "NFCWriter.push should fail with TypeError when invalid negative timeout value is provided.");
+  await promise_rejects(
+    t, new TypeError(), writer.push(test_text_data, { timeout: "invalid"}));
+}, "NFCWriter.push should fail with TypeError when invalid timeout is \
+provided.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
-  return promise_rejects(t, 'TimeoutError', writer.push(test_text_data, { timeout: 1 }));
+  await promise_rejects(
+    t, new TypeError(), writer.push(test_text_data, { timeout: -1 }));
+}, "NFCWriter.push should fail with TypeError when invalid negative timeout \
+value is provided.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  mockNFC.setPendingPushCompleted(false);
+  const p = writer.push(test_text_data, { timeout: 1 });
+  mockNFC.setPushShouldTimeout(true);
+  await promise_rejects(t, 'TimeoutError', p);
 }, "NFCWriter.push should fail with TimeoutError when timer expires.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
-  return promise_rejects(t, 'NotSupportedError', writer.push(new ArrayBuffer(32 * 1024 + 1)));
+  await promise_rejects(
+    t, 'NotSupportedError', writer.push(new ArrayBuffer(32 * 1024 + 1)));
 }, "Reject promise with NotSupportedError if NFC message size exceeds 32KB.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const message = createMessage([createTextRecord(test_text_data)]);
   message.url = '%00/invalid/ path';
-  return promise_rejects(t, 'SyntaxError', writer.push(message));
-}, "Reject promise with SyntaxError if WebNFC Id cannot be created from provided URL.");
+  await promise_rejects(t, 'SyntaxError', writer.push(message));
+}, "Reject promise with SyntaxError if WebNFC Id cannot be created from \
+provided URL.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
   const message = createMessage([createRecord('json','application/json',
       { get x(){ return this; } })]);
-  return promise_rejects(t, 'SyntaxError', writer.push(message));
+  await promise_rejects(t, 'SyntaxError', writer.push(message));
 }, "Reject promise with SyntaxError if 'json' record cannot be serialized.");
 
-promise_test(t => {
+promise_test(async t => {
   const writer = new NFCWriter();
-  return promise_rejects(t, new TypeError(), writer.push(test_text_data, {target: "invalid"}));
-}, "NFCWriter.push should fail with TypeError when invalid target value is provided.");
+  await promise_rejects(
+    t, new TypeError(), writer.push(test_text_data, {target: "invalid"}));
+}, "NFCWriter.push should fail with TypeError when invalid target value is \
+provided.");
 
-promise_test(() => {
-  return new Promise((resolve,reject) => {
+promise_test(async () => {
+  await new Promise((resolve,reject) => {
     const iframe = document.createElement('iframe');
     iframe.srcdoc = `<script>
                       window.onmessage = message => {
                         if (message.data === "Ready") {
-                          const onSuccess = () => { parent.postMessage("Failure", "*"); };
+                          const onSuccess = () => {
+                            parent.postMessage("Failure", "*");
+                          };
                           const onError = error => {
                             if (error.name == "NotAllowedError") {
                               parent.postMessage("Success", "*");
                             } else {
                               parent.postMessage("Failure", "*");
                             }
                           };
                           try {
@@ -206,9 +237,75 @@ promise_test(() => {
         resolve();
       } else if (message.data == 'Failure') {
         reject();
       }
     }
   });
 }, 'Test that WebNFC API is not accessible from iframe context.');
 
+nfc_test(async () => {
+  const writer = new NFCWriter();
+  await writer.push(test_text_data, { timeout: 1 });
+}, 'NFCWriter.push should succeed when NFC HW is enabled');
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  let message = createMessage([createTextRecord(test_text_data),
+                               createJsonRecord(test_json_data),
+                               createOpaqueRecord(test_buffer_data),
+                               createTextRecord(test_number_data),
+                               createUrlRecord(test_url_data)],
+                               test_message_origin);
+  await writer.push(message);
+  assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
+}, "NFCWriter.push NDEFMessage containing text, json, opaque and url records \
+with default NFCPushOptions.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push(test_text_data);
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "Test that NFCWriter.push succeeds when message is DOMString.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push(test_buffer_data);
+  assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
+}, "Test that NFCWriter.push succeeds when message is ArrayBuffer.");
+
+nfc_test(async () => {
+  const writer = new NFCWriter();
+  await writer.push(createMessage([createRecord('empty')]));
+}, "NFCWriter.push with 'empty' record should succeed.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  await writer.push(test_text_data);
+  assertNFCPushOptionsEqual(createNFCPushOptions('any', Infinity, true),
+                            mockNFC.pushOptions());
+}, "Check that default NFCPushOptions values are correctly set.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NFCWriter();
+  let nfcPushOptions = createNFCPushOptions('tag', 1, false);
+  await writer.push(test_text_data, nfcPushOptions);
+  assertNFCPushOptionsEqual(nfcPushOptions, mockNFC.pushOptions());
+}, "Check that provided NFCPushOptions values are correctly converted.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer1 = new NFCWriter();
+  const writer2 = new NFCWriter();
+  const controller = new AbortController();
+  mockNFC.setPendingPushCompleted(false);
+  const p1 = writer1.push(test_text_data,
+      { signal: controller.signal, timeout: 100 });
+
+  // Even though push request is grantable,
+  // this abort should be processed synchronously.
+  controller.abort();
+  await promise_rejects(t, 'AbortError', p1);
+
+  await writer2.push(test_text_data);
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "Synchronously signaled abort.");
+
 </script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/NFCWriter_push_signal-manual.https.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: NFCWriter signal test</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-promise_test(async t => {
-  const writer = new NFCWriter();
-  const reader = new NFCReader();
-  const controller = new AbortController();
-
-  const p1 = writer.push(test_text_data, { signal: controller.signal });
-  const p2 = writer.push(test_text_data);
-
-  // Even though push request is grantable, this abort should be processed synchronously.
-  controller.abort();
-  await promise_rejects(t, 'AbortError', p1);
-  await p2;
-
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  for (let record of event.message.records) {
-    assert_equals(record.data, test_text_data);
-  }
-}, "Synchronously signaled abort.");
-
-</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-nfc/README.md
@@ -0,0 +1,28 @@
+The `nfc-helpers.js` requires an implementation of
+the `WebNFCTest` interfaces, which should emulate platform Web NFC backends.
+
+The `WebNFCTest` interface is defined as:
+
+```
+  class NFCTestChromium {
+    initialize();  // Sets up the testing environment.
+    async reset(); // Frees the resources.
+    getMockNFC(); // Returns `MockNFC` interface.
+  };
+
+  class MockNFC {
+    setHWStatus(number status); // Sets the hardware status.
+    setCompatibility(NDEFCompatibility compatibility); // Sets NDEF accepted compatible devices
+    setReadingMessage(NDEFMessageInit message, NDEFCompatibility compatibility); // Sets message that is used to deliver NFC reading updates with a specific NDEFCompatibility.
+    setPendingPushCompleted(boolean result); // Sets if the pending push is completed.
+    setPushShouldTimeout(boolean result); // Sets flag to trigger the pending push to timeout.
+    pushedMessage(); // Gets the pushed `NDEFMessageSource`.
+    pushOptions(); // Gets the pushed `NFCPushOptions`.
+  };
+```
+
+The Chromium implementation of the `WebNFCTest` interface is located in
+[nfc-mock.js](../resources/chromium/nfc-mock.js).
+
+Other browser vendors should provide their own implementations of
+the `WebNFCTest` interfaces.
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/nfc_hw_disabled-manual.https.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: NFC HW Disabled</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<h2>Precondition</h2>
-<ol>
-  <li>
-    Disable the NFC module or run test on a device without NFC module.
-  </li>
-</ol>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-promise_test(async t => {
-  const reader = new NFCReader();
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("error");
-  assert_equals(event.error.name, 'NotSupportedError');
-}, "NFCReader.start should fail if NFC HW is disabled.");
-
-promise_test(t => {
-  const writer = new NFCWriter();
-  return promise_rejects(t, 'NotSupportedError', writer.push(test_text_data));
-}, "NFCWriter.push should fail when NFC HW is disabled.");
-
-</script>
--- a/testing/web-platform/tests/web-nfc/nfc_insecure_context.html
+++ b/testing/web-platform/tests/web-nfc/nfc_insecure_context.html
@@ -1,16 +1,15 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Web NFC Test: insecure context</title>
 <link rel="author" title="Intel" href="http://www.intel.com"/>
 <link rel="help" href="https://w3c.github.io/web-nfc/"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
 <h2>Note</h2>
 <ol>
   <li>
     Run test is an insecure context, e.g. http://example.com/
   </li>
 </ol>
 <script>
 
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/nfc_push_ArrayBuffer-manual.https.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: push ArrayBuffer message</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-promise_test(async t => {
-  const writer = new NFCWriter();
-  const reader = new NFCReader();
-  await writer.push(test_buffer_data);
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  for (let record of event.message.records) {
-    assert_equals(record.recordType, "opaque");
-    assert_equals(record.mediaType, "application/octet-stream");
-    assert_array_equals(new Uint8Array(record.data), new Uint8Array(test_buffer_data));
-  }
-}, "Test that NFCWriter.push succeeds when message is ArrayBuffer.");
-
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/nfc_push_DOMString-manual.https.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Web NFC Test: push DOMString message</title>
-<link rel="author" title="Intel" href="http://www.intel.com"/>
-<link rel="help" href="https://w3c.github.io/web-nfc/"/>
-<meta name="timeout" content="long">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/nfc_help.js"></script>
-<meta name="flags" content="interact">
-
-<p>Tap an NFC tag to the test device with NFC support.</p>
-
-<p>Note: All the actions need to be done in 60 seconds, otherwise it will get TIMEOUT.</p>
-
-<div id="log"></div>
-
-<script>
-
-"use strict";
-
-setup({ explicit_timeout: true });
-
-promise_test(async t => {
-  const writer = new NFCWriter();
-  const reader = new NFCReader();
-  await writer.push(test_text_data);
-  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
-  reader.start();
-  const event = await readerWatcher.wait_for("reading");
-  for (let record of event.message.records) {
-    assert_equals(record.data, test_text_data);
-  }
-}, "Test that NFCWriter.push succeeds when message is DOMString.");
-
-</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-nfc/resources/nfc-helpers.js
@@ -0,0 +1,206 @@
+'use strict';
+// These tests rely on the User Agent providing an implementation of
+// platform nfc backends.
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+//   --enable-blink-features=MojoJS,MojoJSTest
+let loadChromiumResources = Promise.resolve().then(() => {
+  if (!window.MojoInterfaceInterceptor) {
+    // Do nothing on non-Chromium-based browsers or when the Mojo bindings are
+    // not present in the global namespace.
+    return;
+  }
+
+  let chain = Promise.resolve();
+  [
+    '/gen/layout_test_data/mojo/public/js/mojo_bindings.js',
+    '/gen/services/device/public/mojom/nfc.mojom.js',
+    '/resources/chromium/nfc-mock.js',
+  ].forEach(path => {
+    let script = document.createElement('script');
+    script.src = path;
+    script.async = false;
+    chain = chain.then(() => new Promise(resolve => {
+      script.onload = resolve;
+    }));
+    document.head.appendChild(script);
+  });
+
+  return chain;
+});
+
+async function initialize_nfc_tests() {
+  if (typeof WebNFCTest === 'undefined') {
+    await loadChromiumResources;
+  }
+  assert_true(
+    typeof WebNFCTest !== 'undefined',
+    'WebNFC testing interface is not available.'
+  );
+  let NFCTest = new WebNFCTest();
+  await NFCTest.initialize();
+  return NFCTest;
+}
+
+function nfc_test(func, name, properties) {
+  promise_test(async t => {
+    let NFCTest = await initialize_nfc_tests();
+    let mockTest = NFCTest.getMockNFC();
+    try {
+      await func(t, mockTest);
+    } finally {
+      await NFCTest.reset();
+    };
+  }, name, properties);
+}
+
+const test_text_data = 'Test text data.';
+const test_text_byte_array = new TextEncoder('utf-8').encode(test_text_data);
+const test_number_data = 42;
+const test_json_data = {level: 1, score: 100, label: 'Game'};
+const test_url_data = 'https://w3c.github.io/web-nfc/';
+const test_message_origin = 'https://127.0.0.1:8443';
+const test_buffer_data = new ArrayBuffer(test_text_byte_array.length);
+const test_buffer_view =
+    new Uint8Array(test_buffer_data).set(test_text_byte_array);
+const fake_tag_serial_number = 'c0:45:00:02';
+
+const NFCHWStatus = {};
+// OS-level NFC setting is ON
+NFCHWStatus.ENABLED = 1;
+// no NFC chip
+NFCHWStatus.NOT_SUPPORTED = NFCHWStatus.ENABLED + 1;
+// OS-level NFC setting OFF
+NFCHWStatus.DISABLED = NFCHWStatus.NOT_SUPPORTED + 1;
+
+function createMessage(records) {
+  if (records !== undefined) {
+    let message = {};
+    message.records = records;
+    return message;
+  }
+}
+
+function createRecord(recordType, mediaType, data) {
+  let record = {};
+  if (recordType !== undefined)
+    record.recordType = recordType;
+  if (mediaType !== undefined)
+    record.mediaType = mediaType;
+  if (data !== undefined)
+    record.data = data;
+  return record;
+}
+
+function createTextRecord(text) {
+  return createRecord('text', 'text/plain', text);
+}
+
+function createJsonRecord(json) {
+  return createRecord('json', 'application/json', json);
+}
+
+function createOpaqueRecord(buffer) {
+  return createRecord('opaque', 'application/octet-stream', buffer);
+}
+
+function createUrlRecord(url) {
+  return createRecord('url', 'text/plain', url);
+}
+
+function createNFCPushOptions(target, timeout, ignoreRead, compatibility) {
+  return { target, timeout, ignoreRead, compatibility};
+}
+
+// Compares NDEFMessageSource that was provided to the API
+// (e.g. NFCWriter.push), and NDEFMessage that was received by the
+// mock NFC service.
+function assertNDEFMessagesEqual(providedMessage, receivedMessage) {
+  // If simple data type is passed, e.g. String or ArrayBuffer, convert it
+  // to NDEFMessage before comparing.
+  // https://w3c.github.io/web-nfc/#dom-ndefmessagesource
+  let provided = providedMessage;
+  if (providedMessage instanceof ArrayBuffer)
+    provided = createMessage([createOpaqueRecord(providedMessage)]);
+  else if (typeof providedMessage === 'string')
+    provided = createMessage([createTextRecord(providedMessage)]);
+
+  assert_equals(provided.records.length, receivedMessage.data.length,
+      'NDEFMessages must have same number of NDEFRecords');
+
+  // Compare contents of each individual NDEFRecord
+  for (let i = 0; i < provided.records.length; ++i)
+    compareNDEFRecords(provided.records[i], receivedMessage.data[i]);
+}
+
+// Used to compare two WebNFC messages, one that is provided to mock NFC
+// service and another that is received from NFCWriter.onreading() EventHandler.
+function assertWebNDEFMessagesEqual(a, b) {
+  if (b.url) assert_equals(a.url, b.url);
+  assert_equals(a.records.length, b.records.length);
+  for(let i in a.records) {
+    let recordA = a.records[i];
+    let recordB = b.records[i];
+    assert_equals(recordA.recordType, recordB.recordType);
+    assert_equals(recordA.mediaType, recordB.mediaType);
+    if (recordA.data() == null) {
+      assert_true(recordB.data == null);
+    } else if (recordA.data() instanceof ArrayBuffer) {
+      assert_array_equals(new Uint8Array(recordA.data()),
+          new Uint8Array(recordB.data));
+    } else if (typeof recordA.data() === 'object') {
+      assert_object_equals(recordA.data(), recordB.data);
+    } else if (typeof recordA.data() === 'number'
+        || typeof recordA.data() === 'string') {
+      assert_true(recordA.data() == recordB.data);
+    }
+  }
+}
+
+function testNFCReaderOptions(message, readOptions, unmatchedReadOptions, desc) {
+  nfc_test(async (t, mockNFC) => {
+    const reader1 = new NFCReader(unmatchedReadOptions);
+    const reader2 = new NFCReader(readOptions);
+
+    mockNFC.setReadingMessage(message, readOptions.compatibility);
+
+    // Reading from unmatched reader will not be triggered
+    reader1.onreading = t.unreached_func("reading event should not be fired.");
+    reader1.start();
+
+    const readerWatcher = new EventWatcher(t, reader2, ["reading", "error"]);
+
+    const promise = readerWatcher.wait_for("reading").then(event => {
+      reader1.stop();
+      reader2.stop();
+      assertWebNDEFMessagesEqual(event.message, message);
+    });
+    // NFCReader#start() asynchronously dispatches the onreading event.
+    reader2.start();
+    await promise;
+  }, desc);
+}
+
+function testReadingMultiMessages(message, readOptions, unmatchedMessage,
+    unmatchedCompatibility, desc) {
+  nfc_test(async (t, mockNFC) => {
+    const reader = new NFCReader(readOptions);
+    const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+    const promise = readerWatcher.wait_for("reading").then(event => {
+      reader.stop();
+      assertWebNDEFMessagesEqual(event.message, message);
+    });
+    // NFCReader#start() asynchronously dispatches the onreading event.
+    reader.start();
+
+    // Unmatched message will not be read
+    mockNFC.setReadingMessage(unmatchedMessage, unmatchedCompatibility);
+    mockNFC.setReadingMessage(message);
+
+    await promise;
+  }, desc);
+}
deleted file mode 100644
--- a/testing/web-platform/tests/web-nfc/resources/nfc_help.js
+++ /dev/null
@@ -1,86 +0,0 @@
-'use strict';
-
-const test_text_data = "Test text data.";
-const test_text_byte_array = new TextEncoder('utf-8').encode(test_text_data);
-const test_number_data = 42;
-const test_json_data = {level: 1, score: 100, label: 'Game'};
-const test_url_data = "https://w3c.github.io/web-nfc";
-const test_buffer_data = new ArrayBuffer(test_text_byte_array.length);
-const test_buffer_view = new Uint8Array(test_buffer_data).set(test_text_byte_array);
-
-function noop() {};
-
-function createMessage(records) {
-  if (records !== undefined) {
-    let message = {};
-    message.records = records;
-    return message;
-  }
-}
-
-function createRecord(recordType, mediaType, data) {
-  let record = {};
-  if (recordType !== undefined) {
-    record.recordType = recordType;
-  }
-  if (mediaType !== undefined) {
-    record.mediaType = mediaType;
-  }
-  if (data !== undefined) {
-    record.data = data;
-  }
-  return record;
-}
-
-function createTextRecord(text) {
-  return createRecord('text', 'text/plain', text);
-}
-
-function createJsonRecord(json) {
-  return createRecord('json', 'application/json', json);
-}
-
-function createOpaqueRecord(buffer) {
-  return createRecord('opaque', 'application/octet-stream', buffer);
-}
-
-function createUrlRecord(url) {
-  return createRecord('url', 'text/plain', url);
-}
-
-function assertWebNDEFMessagesEqual(a, b) {
-  if (b.url) assert_equals(a.url, `${location.origin}${b.url}`);
-  assert_equals(a.records.length, b.records.length);
-  for(let i in a.records) {
-    let recordA = a.records[i];
-    let recordB = b.records[i];
-    assert_equals(recordA.recordType, recordB.recordType);
-    assert_equals(recordA.mediaType, recordB.mediaType);
-    if (recordA.data() instanceof ArrayBuffer) {
-      assert_array_equals(new Uint8Array(recordA.data()),
-          new Uint8Array(recordB.data()));
-    } else if (typeof recordA.data() === 'object') {
-      assert_object_equals(recordA.data(), recordB.data());
-    } else if (typeof recordA.data() === 'number'
-        || typeof recordA.data() === 'string') {
-      assert_true(recordA.data() == recordB.data());
-    }
-  }
-}
-
-function testNFCReaderOptions(pushedMessage, readOptions, unacceptableReadOptions, desc) {
-  promise_test(async t => {
-    const writer = new NFCWriter();
-    const reader1 = new NFCReader(unacceptableReadOptions);
-    const reader2 = new NFCReader(readOptions);
-    await writer.push(pushedMessage);
-
-    reader1.onreading = t.unreached_func("reading event should not be fired.");
-    reader1.start();
-
-    const readerWatcher = new EventWatcher(t, reader2, ["reading", "error"]);
-    reader2.start();
-    const event = await readerWatcher.wait_for("reading");
-    assertWebNDEFMessagesEqual(event.message, pushedMessage);
-  }, desc);
-}
--- a/toolkit/components/viewconfig/content/config.js
+++ b/toolkit/components/viewconfig/content/config.js
@@ -145,19 +145,16 @@ var view = {
   },
   selectionChanged() {},
   cycleCell(row, col) {},
   isEditable(row, col) {
     return false;
   },
   setCellValue(row, col, value) {},
   setCellText(row, col, value) {},
-  performAction(action) {},
-  performActionOnRow(action, row) {},
-  performActionOnCell(action, row, col) {},
   isSeparator(index) {
     return false;
   },
 };
 
 // find the index in gPrefView of a pref object
 // or -1 if it does not exist in the filtered view
 function getViewIndexOfPref(pref) {