Merge inbound to mozilla-central. a=merge
authorMihai Alexandru Michis <malexandru@mozilla.com>
Wed, 31 Jul 2019 12:49:24 +0300
changeset 485564 0cfffbaad82ed31e5e61885cf9811bf149a0408c
parent 485460 968808feda5ae1791106316e63236e3346bbc812 (current diff)
parent 485563 9348074eab097da1507acc4c6ffbcd0d52b5fee1 (diff)
child 485565 2ac4e7a2410398b1abfea9d665dba1038958aeff
child 485613 f905e06aac2b9fdcade9afcd4ae59a9a66443dcc
push id91319
push usermalexandru@mozilla.com
push dateWed, 31 Jul 2019 09:54:43 +0000
treeherderautoland@2ac4e7a24103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
0cfffbaad82e / 70.0a1 / 20190731095029 / files
nightly linux64
0cfffbaad82e / 70.0a1 / 20190731095029 / files
nightly mac
0cfffbaad82e / 70.0a1 / 20190731095029 / files
nightly win32
0cfffbaad82e / 70.0a1 / 20190731095029 / files
nightly win64
0cfffbaad82e / 70.0a1 / 20190731095029 / 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/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.html.ini
testing/web-platform/tests/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-006.html
testing/web-platform/tests/css/css-transforms/parsing/perspective-origin-parsing-invalid.html
testing/web-platform/tests/css/css-transforms/parsing/perspective-origin-parsing-valid.html
testing/web-platform/tests/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.html
testing/web-platform/tests/interfaces/xslt.idl
testing/web-platform/tests/xslt/idlharness.window.js
--- a/js/src/gc/GCMarker.h
+++ b/js/src/gc/GCMarker.h
@@ -226,21 +226,18 @@ class MarkStackIter {
 
 } /* namespace gc */
 
 class GCMarker : public JSTracer {
  public:
   explicit GCMarker(JSRuntime* rt);
   MOZ_MUST_USE bool init(JSGCMode gcMode);
 
-  void setMaxCapacity(size_t maxCap) {
-    blackStack.setMaxCapacity(maxCap);
-    grayStack.setMaxCapacity(maxCap);
-  }
-  size_t maxCapacity() const { return blackStack.maxCapacity(); }
+  void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
+  size_t maxCapacity() const { return stack.maxCapacity(); }
 
   void start();
   void stop();
   void reset();
 
   // Mark the given GC thing and traverse its children at some point.
   template <typename T>
   void traverse(T thing);
@@ -306,20 +303,17 @@ class GCMarker : public JSTracer {
     QueueYielded,   // End this incremental GC slice, if possible
     QueueComplete,  // Done with the queue
     QueueSuspended  // Continue the GC without ending the slice
   };
   MarkQueueProgress processMarkQueue();
 
   MOZ_MUST_USE bool markUntilBudgetExhausted(SliceBudget& budget);
 
-  void setGCMode(JSGCMode mode) {
-    blackStack.setGCMode(mode);
-    grayStack.setGCMode(mode);
-  }
+  void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
 
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #ifdef DEBUG
   bool shouldCheckCompartments() { return strictCompartmentChecking; }
 #endif
 
   void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
@@ -371,23 +365,21 @@ class GCMarker : public JSTracer {
   template <typename T>
   MOZ_MUST_USE bool mark(T* thing);
 
   template <typename T>
   inline void pushTaggedPtr(T* ptr);
 
   inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
 
-  bool isMarkStackEmpty() {
-    return blackStack.isEmpty() && grayStack.isEmpty();
-  }
+  bool isMarkStackEmpty() { return stack.isEmpty(); }
 
-  bool hasBlackEntries() const { return !blackStack.isEmpty(); }
+  bool hasBlackEntries() const { return stack.position() > grayPosition; }
 
-  bool hasGrayEntries() const { return !grayStack.isEmpty(); }
+  bool hasGrayEntries() const { return grayPosition > 0 && !stack.isEmpty(); }
 
   MOZ_MUST_USE bool restoreValueArray(
       const gc::MarkStack::SavedValueArray& array, HeapSlot** vpp,
       HeapSlot** endp);
   gc::MarkStack::ValueArray restoreValueArray(
       const gc::MarkStack::SavedValueArray& savedArray);
 
   void saveValueRanges();
@@ -401,28 +393,25 @@ class GCMarker : public JSTracer {
   bool processDelayedMarkingList(gc::MarkColor color, SliceBudget& budget);
   bool hasDelayedChildren() const { return !!delayedMarkingList; }
   void rebuildDelayedMarkingList();
   void appendToDelayedMarkingList(gc::Arena** listTail, gc::Arena* arena);
 
   template <typename F>
   void forEachDelayedMarkingArena(F&& f);
 
-  /* The stack of items to mark black. */
-  gc::MarkStack blackStack;
-  /* The stack of items to mark (CC) gray. */
-  gc::MarkStack grayStack;
+  /* The mark stack. Pointers in this stack are "gray" in the GC sense. */
+  gc::MarkStack stack;
+
+  /* Stack entries at positions below this are considered gray. */
+  MainThreadData<size_t> grayPosition;
 
   /* The color is only applied to objects and functions. */
   MainThreadData<gc::MarkColor> color;
 
-  gc::MarkStack& currentStack() {
-    return color == gc::MarkColor::Black ? blackStack : grayStack;
-  }
-
   /* Pointer to the top of the stack of arenas we are delaying marking on. */
   MainThreadData<js::gc::Arena*> delayedMarkingList;
 
   /* Whether more work has been added to the delayed marking list. */
   MainThreadData<bool> delayedMarkingWorkAdded;
 
   /*
    * If the weakKeys table OOMs, disable the linear algorithm and fall back
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1126,17 +1126,16 @@ inline void js::GCMarker::eagerlyMarkChi
   // This function tries to scan the whole rope tree using the marking stack
   // as temporary storage. If that becomes full, the unscanned ropes are
   // added to the delayed marking list. When the function returns, the
   // marking stack is at the same depth as it was on entry. This way we avoid
   // using tags when pushing ropes to the stack as ropes never leak to other
   // users of the stack. This also assumes that a rope can only point to
   // other ropes or linear strings, it cannot refer to GC things of other
   // types.
-  gc::MarkStack& stack = currentStack();
   size_t savedPos = stack.position();
   JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
 #ifdef JS_DEBUG
   static const size_t DEEP_ROPE_THRESHOLD = 100000;
   static const size_t ROPE_CYCLE_HISTORY = 100;
   DebugOnly<size_t> ropeDepth = 0;
   JSRope* history[ROPE_CYCLE_HISTORY];
 #endif
@@ -1619,35 +1618,24 @@ bool GCMarker::markUntilBudgetExhausted(
       if (budget.isOverBudget()) {
         return false;
       }
     }
 
     if (hasGrayEntries()) {
       AutoSetMarkColor autoSetGray(*this, MarkColor::Gray);
       do {
+        MOZ_ASSERT(!hasBlackEntries());
         processMarkStackTop(budget);
         if (budget.isOverBudget()) {
           return false;
         }
       } while (hasGrayEntries());
     }
 
-    if (hasBlackEntries()) {
-      // We can end up marking black during gray marking in the following case:
-      // we have a WeakMap with a CCW key whose delegate is black, and during
-      // gray marking we mark the map (gray). The delegate's color will be
-      // propagated to the key. (And we can't avoid this by marking the key
-      // gray, because even though the value will end up gray in either case,
-      // the WeakMap entry must be (strongly) preserved because the CCW could
-      // get collected and then we could re-wrap the delegate and look it up in
-      // the map again.)
-      continue;
-    }
-
     if (!hasDelayedChildren()) {
       break;
     }
 
     /*
      * Mark children of things that caused too deep recursion during the
      * above tracing. Don't do this until we're done with everything
      * else.
@@ -1718,18 +1706,16 @@ inline void GCMarker::processMarkStackTo
    * The function uses explicit goto and implements the scanning of the
    * object directly. It allows to eliminate the tail recursion and
    * significantly improve the marking performance, see bug 641025.
    */
   HeapSlot* vp;
   HeapSlot* end;
   JSObject* obj;
 
-  gc::MarkStack& stack = currentStack();
-
   switch (stack.peekTag()) {
     case MarkStack::ValueArrayTag: {
       auto array = stack.popValueArray();
       obj = array.ptr.asValueArrayObject();
       vp = array.start;
       end = array.end;
       goto scan_value_array;
     }
@@ -1886,36 +1872,33 @@ scan_obj : {
  * the entire stack. At that point, JS code can run and reallocate slot arrays
  * that are stored on the stack. To prevent this from happening, we replace all
  * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots
  * pointers are replaced with slot indexes, and slot array end pointers are
  * replaced with the kind of index (properties vs. elements).
  */
 
 void GCMarker::saveValueRanges() {
-  gc::MarkStack* stacks[2] = {&blackStack, &grayStack};
-  for (auto& stack : stacks) {
-    MarkStackIter iter(*stack);
-    while (!iter.done()) {
-      auto tag = iter.peekTag();
-      if (tag == MarkStack::ValueArrayTag) {
-        const auto& array = iter.peekValueArray();
-        auto savedArray = saveValueRange(array);
-        iter.saveValueArray(savedArray);
-        iter.nextArray();
-      } else if (tag == MarkStack::SavedValueArrayTag) {
-        iter.nextArray();
-      } else {
-        iter.nextPtr();
-      }
+  MarkStackIter iter(stack);
+  while (!iter.done()) {
+    auto tag = iter.peekTag();
+    if (tag == MarkStack::ValueArrayTag) {
+      const auto& array = iter.peekValueArray();
+      auto savedArray = saveValueRange(array);
+      iter.saveValueArray(savedArray);
+      iter.nextArray();
+    } else if (tag == MarkStack::SavedValueArrayTag) {
+      iter.nextArray();
+    } else {
+      iter.nextPtr();
     }
-
-    // This is also a convenient point to poison unused stack memory.
-    stack->poisonUnused();
   }
+
+  // This is also a convenient point to poison unused stack memory.
+  stack.poisonUnused();
 }
 
 bool GCMarker::restoreValueArray(const MarkStack::SavedValueArray& savedArray,
                                  HeapSlot** vpp, HeapSlot** endp) {
   JSObject* objArg = savedArray.ptr.asSavedValueArrayObject();
   if (!objArg->isNative()) {
     return false;
   }
@@ -2381,35 +2364,33 @@ void MarkStackIter::saveValueArray(
 /*
  * ExpandWeakMaps: the GC is recomputing the liveness of WeakMap entries by
  * expanding each live WeakMap into its constituent key->value edges, a table
  * of which will be consulted in a later phase whenever marking a potential
  * key.
  */
 GCMarker::GCMarker(JSRuntime* rt)
     : JSTracer(rt, JSTracer::TracerKindTag::Marking, ExpandWeakMaps),
-      blackStack(),
-      grayStack(),
+      stack(),
+      grayPosition(0),
       color(MarkColor::Black),
       delayedMarkingList(nullptr),
       delayedMarkingWorkAdded(false)
 #ifdef DEBUG
       ,
       markLaterArenas(0),
       started(false),
       strictCompartmentChecking(false),
       markQueue(rt),
       queuePos(0)
 #endif
 {
 }
 
-bool GCMarker::init(JSGCMode gcMode) {
-  return blackStack.init(gcMode) && grayStack.init(gcMode);
-}
+bool GCMarker::init(JSGCMode gcMode) { return stack.init(gcMode); }
 
 void GCMarker::start() {
 #ifdef DEBUG
   MOZ_ASSERT(!started);
   started = true;
 #endif
   color = MarkColor::Black;
   linearWeakMarkingDisabled_ = false;
@@ -2430,18 +2411,17 @@ void GCMarker::stop() {
   MOZ_ASSERT(started);
   started = false;
 
   MOZ_ASSERT(!delayedMarkingList);
   MOZ_ASSERT(markLaterArenas == 0);
 #endif
 
   /* Free non-ballast stack memory. */
-  blackStack.clear();
-  grayStack.clear();
+  stack.clear();
   AutoEnterOOMUnsafeRegion oomUnsafe;
   for (GCZonesIter zone(runtime()); !zone.done(); zone.next()) {
     if (!zone->gcWeakKeys().clear()) {
       oomUnsafe.crash("clearing weak keys in GCMarker::stop()");
     }
     if (!zone->gcNurseryWeakKeys().clear()) {
       oomUnsafe.crash("clearing (nursery) weak keys in GCMarker::stop()");
     }
@@ -2457,18 +2437,17 @@ inline void GCMarker::forEachDelayedMark
     f(arena);
     arena = next;
   }
 }
 
 void GCMarker::reset() {
   color = MarkColor::Black;
 
-  blackStack.clear();
-  grayStack.clear();
+  stack.clear();
   MOZ_ASSERT(isMarkStackEmpty());
 
   forEachDelayedMarkingArena([&](Arena* arena) {
     MOZ_ASSERT(arena->onDelayedMarkingList());
     arena->clearDelayedMarkingState();
 #ifdef DEBUG
     MOZ_ASSERT(markLaterArenas);
     markLaterArenas--;
@@ -2487,45 +2466,49 @@ void GCMarker::setMarkColor(gc::MarkColo
   if (newColor == gc::MarkColor::Black) {
     setMarkColorBlack();
   } else {
     setMarkColorGray();
   }
 }
 
 void GCMarker::setMarkColorGray() {
+  MOZ_ASSERT(!hasBlackEntries());
   MOZ_ASSERT(color == gc::MarkColor::Black);
   MOZ_ASSERT(runtime()->gc.state() == State::Sweep);
 
   color = gc::MarkColor::Gray;
+  grayPosition = SIZE_MAX;
 }
 
 void GCMarker::setMarkColorBlack() {
+  MOZ_ASSERT(!hasBlackEntries());
   MOZ_ASSERT(color == gc::MarkColor::Gray);
   MOZ_ASSERT(runtime()->gc.state() == State::Sweep);
 
   color = gc::MarkColor::Black;
+  grayPosition = stack.position();
 }
 
 template <typename T>
 void GCMarker::pushTaggedPtr(T* ptr) {
   checkZone(ptr);
-  if (!currentStack().push(ptr)) {
+  if (!stack.push(ptr)) {
     delayMarkingChildren(ptr);
   }
 }
 
 void GCMarker::pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end) {
   checkZone(obj);
 
   if (start == end) {
     return;
   }
 
-  if (!currentStack().push(obj, start, end)) {
+  if (!stack.push(obj, start, end)) {
     delayMarkingChildren(obj);
   }
 }
 
 void GCMarker::repush(JSObject* obj) {
   MOZ_ASSERT(obj->asTenured().isMarkedAtLeast(markColor()));
   pushTaggedPtr(obj);
 }
@@ -2727,18 +2710,17 @@ void gc::PushArena(GCMarker* gcmarker, A
 void GCMarker::checkZone(void* p) {
   MOZ_ASSERT(started);
   DebugOnly<Cell*> cell = static_cast<Cell*>(p);
   MOZ_ASSERT_IF(cell->isTenured(), cell->asTenured().zone()->isCollecting());
 }
 #endif
 
 size_t GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
-  size_t size = blackStack.sizeOfExcludingThis(mallocSizeOf);
-  size += grayStack.sizeOfExcludingThis(mallocSizeOf);
+  size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
   for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next()) {
     size += zone->gcGrayRoots().SizeOfExcludingThis(mallocSizeOf);
   }
   return size;
 }
 
 /*** Tenuring Tracer ********************************************************/
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -1,18 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Zone-inl.h"
 
-#include "jsutil.h"
-
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitRealm.h"
 #include "vm/Runtime.h"
--- a/taskcluster/taskgraph/transforms/job/toolchain.py
+++ b/taskcluster/taskgraph/transforms/job/toolchain.py
@@ -7,21 +7,23 @@ Support for running toolchain-building j
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from mozbuild.shellutil import quote as shell_quote
 
 from taskgraph.util.schema import Schema
 from voluptuous import Optional, Required, Any
 
-from taskgraph.transforms.job import run_job_using
+from taskgraph.transforms.job import (
+    configure_taskdesc_for_run,
+    run_job_using,
+)
 from taskgraph.transforms.job.common import (
     docker_worker_add_artifacts,
     docker_worker_add_tooltool,
-    generic_worker_hg_commands,
     support_vcs_checkout,
 )
 from taskgraph.util.hash import hash_paths
 from taskgraph import GECKO
 import taskgraph
 
 
 CACHE_TYPE = 'toolchains.v3'
@@ -64,17 +66,17 @@ toolchain_run_schema = Schema({
     Optional('toolchain-alias'): basestring,
 
     # Base work directory used to set up the task.
     Required('workdir'): basestring,
 })
 
 
 def get_digest_data(config, run, taskdesc):
-    files = list(run.get('resources', []))
+    files = list(run.pop('resources', []))
     # This file
     files.append('taskcluster/taskgraph/transforms/job/toolchain.py')
     # The script
     files.append('taskcluster/scripts/misc/{}'.format(run['script']))
     # Tooltool manifest if any is defined:
     tooltool_manifest = taskdesc['worker']['env'].get('TOOLTOOL_MANIFEST')
     if tooltool_manifest:
         files.append(tooltool_manifest)
@@ -184,67 +186,59 @@ def docker_worker_toolchain(config, job,
         }
 
 
 @run_job_using("generic-worker", "toolchain-script",
                schema=toolchain_run_schema, defaults=toolchain_defaults)
 def windows_toolchain(config, job, taskdesc):
     run = job['run']
 
-    worker = taskdesc['worker']
+    worker = taskdesc['worker'] = job['worker']
 
     worker['artifacts'] = [{
         'path': r'public\build',
         'type': 'directory',
     }]
     worker['chain-of-trust'] = True
 
     # There were no caches on generic-worker before bug 1519472, and they cause
     # all sorts of problems with toolchain tasks, disable them until
     # tasks are ready.
     run['use-caches'] = False
-    support_vcs_checkout(config, job, taskdesc, sparse=('sparse-profile' in run))
 
     env = worker['env']
     env.update({
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'MOZ_AUTOMATION': '1',
     })
 
-    sparse_profile = run.get('sparse-profile')
-    if sparse_profile:
-        sparse_profile = 'build/sparse-profiles/{}'.format(run['sparse-profile'])
-
-    hg_command = generic_worker_hg_commands(
-        'https://hg.mozilla.org/mozilla-unified',
-        env['GECKO_HEAD_REPOSITORY'],
-        env['GECKO_HEAD_REV'],
-        r'.\build\src', sparse_profile=sparse_profile)[0]
-
     # Use `mach` to invoke python scripts so in-tree libraries are available.
     if run['script'].endswith('.py'):
         raise NotImplementedError("Python scripts don't work on Windows")
 
     args = run.get('arguments', '')
     if args:
         args = ' ' + shell_quote(*args)
 
-    bash = r'c:\mozilla-build\msys\bin\bash'
-    worker['command'] = [
-        hg_command,
-        # do something intelligent.
-        r'{} build/src/taskcluster/scripts/misc/{}{}'.format(
-            bash, run['script'], args)
-    ]
-
     attributes = taskdesc.setdefault('attributes', {})
-    attributes['toolchain-artifact'] = run['toolchain-artifact']
+    attributes['toolchain-artifact'] = run.pop('toolchain-artifact')
     if 'toolchain-alias' in run:
-        attributes['toolchain-alias'] = run['toolchain-alias']
+        attributes['toolchain-alias'] = run.pop('toolchain-alias')
 
     if not taskgraph.fast:
         name = taskdesc['label'].replace('{}-'.format(config.kind), '', 1)
         taskdesc['cache'] = {
             'type': CACHE_TYPE,
             'name': name,
             'digest-data': get_digest_data(config, run, taskdesc),
         }
+
+    bash = r'c:\mozilla-build\msys\bin\bash'
+
+    run['using'] = 'run-task'
+    run['command'] = [
+        # do something intelligent.
+        r'{} build/src/taskcluster/scripts/misc/{}{}'.format(
+            bash, run.pop('script'), args)
+    ]
+    run.pop('arguments', None)
+    configure_taskdesc_for_run(config, job, taskdesc, worker['implementation'])
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/animation-worklet/worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html.ini
@@ -0,0 +1,4 @@
+[worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html]
+  [WorkletAnimation effect should recompute its calculated timing if its local time changes]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-box/parsing/margin-computed.html.ini
@@ -0,0 +1,16 @@
+[margin-computed.html]
+  [Property margin value '10px' computes to '10px']
+    expected: FAIL
+
+  [Property margin-right value '20px' computes to '20px']
+    expected: FAIL
+
+  [Property margin value '30%' computes to '60px']
+    expected: FAIL
+
+  [Property margin value '10px 20px 30px 40px' computes to '10px 20px 30px 40px']
+    expected: FAIL
+
+  [Property margin value 'calc(0.5em + 10px)' computes to '30px']
+    expected: FAIL
+
--- a/testing/web-platform/meta/css/css-properties-values-api/idlharness.html.ini
+++ b/testing/web-platform/meta/css/css-properties-values-api/idlharness.html.ini
@@ -1,7 +1,46 @@
 [idlharness.html]
   [CSS interface: operation registerProperty(PropertyDescriptor)]
     expected: FAIL
 
   [CSS namespace: operation registerProperty(PropertyDescriptor)]
     expected: FAIL
 
+  [CSSPropertyRule interface: attribute inherits]
+    expected: FAIL
+
+  [CSSPropertyRule interface: existence and properties of interface prototype object's @@unscopables property]
+    expected: FAIL
+
+  [CSSRule interface: constant PROPERTY_RULE on interface prototype object]
+    expected: FAIL
+
+  [CSSPropertyRule interface object name]
+    expected: FAIL
+
+  [CSSPropertyRule interface: attribute initialValue]
+    expected: FAIL
+
+  [CSSPropertyRule interface: existence and properties of interface prototype object's "constructor" property]
+    expected: FAIL
+
+  [CSSRule interface: constant PROPERTY_RULE on interface object]
+    expected: FAIL
+
+  [CSSPropertyRule interface object length]
+    expected: FAIL
+
+  [CSSPropertyRule interface: attribute syntax]
+    expected: FAIL
+
+  [CSSPropertyRule interface: attribute name]
+    expected: FAIL
+
+  [CSS namespace: operation registerProperty(PropertyDefinition)]
+    expected: FAIL
+
+  [CSSPropertyRule interface: existence and properties of interface object]
+    expected: FAIL
+
+  [CSSPropertyRule interface: existence and properties of interface prototype object]
+    expected: FAIL
+
--- a/testing/web-platform/meta/css/css-properties-values-api/register-property-syntax-parsing.html.ini
+++ b/testing/web-platform/meta/css/css-properties-values-api/register-property-syntax-parsing.html.ini
@@ -522,8 +522,14 @@
     expected: FAIL
 
   [syntax:'foo foo foo', initialValue:'foo foo foo' is invalid]
     expected: FAIL
 
   [syntax:'foo \\1F914 bar', initialValue:'foo \\1F914 bar' is invalid]
     expected: FAIL
 
+  [syntax:'<percentage> | <length>+', initialValue:'calc(100vh - 10px) 30px' is valid]
+    expected: FAIL
+
+  [syntax:'<length>', initialValue:'10vmin' is valid]
+    expected: FAIL
+
--- a/testing/web-platform/meta/css/css-scroll-snap/scroll-snap-type-on-root-element.html.ini
+++ b/testing/web-platform/meta/css/css-scroll-snap/scroll-snap-type-on-root-element.html.ini
@@ -1,9 +1,14 @@
 [scroll-snap-type-on-root-element.html]
   [The writing-mode on the body is used]
-    expected:
-      if (os == "android") and e10s: FAIL
+    expected: FAIL
 
   [The scroll-snap-type on the root element is applied]
     expected:
       if (os == "android") and e10s: FAIL
 
+  [The writing-mode (vertical-lr) on the body is used]
+    expected: FAIL
+
+  [The writing-mode (horizontal-tb) on the body is used ]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/ja/css-text-line-break-ja-pr-normal.html.ini
@@ -0,0 +1,25 @@
+[css-text-line-break-ja-pr-normal.html]
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [00B1  PLUS-MINUS SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if ja and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/ja/css-text-line-break-ja-pr-strict.html.ini
@@ -0,0 +1,25 @@
+[css-text-line-break-ja-pr-strict.html]
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [00B1  PLUS-MINUS SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if ja and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-loose.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-de-hyphens-loose.html]
+  [2013 EN DASH may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2010 HYPHEN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if de and loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-normal.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-de-hyphens-normal.html]
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2010 HYPHEN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2013 EN DASH may NOT appear at line start if de and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-strict.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-de-hyphens-strict.html]
+  [2010 HYPHEN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2013 EN DASH may NOT appear at line start if de and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-po-loose.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-de-po-loose.html]
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2032  PRIME may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2030  PER MILLE SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2033  DOUBLE PRIME may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if de and loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-po-normal.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-de-po-normal.html]
+  [2032  PRIME may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2033  DOUBLE PRIME may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2030  PER MILLE SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-po-strict.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-de-po-strict.html]
+  [2030  PER MILLE SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2033  DOUBLE PRIME may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2032  PRIME may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if de and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-pr-loose.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-de-pr-loose.html]
+  [2116  NUMERO SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if de and loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-pr-normal.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-de-pr-normal.html]
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if de and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/other-lang/css-text-line-break-de-pr-strict.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-de-pr-strict.html]
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if de and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-loose.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-hyphens-loose.html]
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if lang unknown and loose]
+    expected: FAIL
+
+  [2010 HYPHEN may NOT appear at line start if lang unknown and loose]
+    expected: FAIL
+
+  [2013 EN DASH may NOT appear at line start if lang unknown and loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-normal.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-hyphens-normal.html]
+  [2013 EN DASH may NOT appear at line start if lang unkonwn and normal]
+    expected: FAIL
+
+  [2010 HYPHEN may NOT appear at line start if lang unkonwn and normal]
+    expected: FAIL
+
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if lang unkonwn and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-strict.html.ini
@@ -0,0 +1,10 @@
+[css-text-line-break-hyphens-strict.html]
+  [30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN may NOT appear at line start if lang unknown and strict]
+    expected: FAIL
+
+  [2013 EN DASH may NOT appear at line start if lang unknown and strict]
+    expected: FAIL
+
+  [2010 HYPHEN may NOT appear at line start if lang unknown and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-po-loose.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-po-loose.html]
+  [2033  DOUBLE PRIME may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2032  PRIME may NOT appear at line start if loose]
+    expected: FAIL
+
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2030  PER MILLE SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-po-normal.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-po-normal.html]
+  [2033  DOUBLE PRIME may NOT appear at line start if normal]
+    expected: FAIL
+
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2032  PRIME may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2030  PER MILLE SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-po-strict.html.ini
@@ -0,0 +1,22 @@
+[css-text-line-break-po-strict.html]
+  [2033  DOUBLE PRIME may NOT appear at line start if strict]
+    expected: FAIL
+
+  [2032  PRIME may NOT appear at line start if strict]
+    expected: FAIL
+
+  [FE6A  SMALL PERCENT SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [2035  REVERSED PRIME may NOT appear at line start if strict]
+    expected: FAIL
+
+  [2030  PER MILLE SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [2103  DEGREE CELSIUS may NOT appear at line start if strict]
+    expected: FAIL
+
+  [2109  DEGREE FAHRENHEIT may NOT appear at line start if strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-pr-loose.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-pr-loose.html]
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if loose]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-pr-normal.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-pr-normal.html]
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/unknown-lang/css-text-line-break-pr-strict.html.ini
@@ -0,0 +1,19 @@
+[css-text-line-break-pr-strict.html]
+  [2116  NUMERO SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/zh/css-text-line-break-zh-pr-normal.html.ini
@@ -0,0 +1,25 @@
+[css-text-line-break-zh-pr-normal.html]
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [00B1  PLUS-MINUS SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
+  [FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if zh and normal]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/i18n/zh/css-text-line-break-zh-pr-strict.html.ini
@@ -0,0 +1,25 @@
+[css-text-line-break-zh-pr-strict.html]
+  [FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [2116  NUMERO SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [FE69  SMALL DOLLAR SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [00B1  PLUS-MINUS SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [FFE6  FULLWIDTH WON SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [20AC  EURO SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
+  [FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if zh and strict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/white-space/break-spaces-tab-004.html.ini
@@ -0,0 +1,2 @@
+[break-spaces-tab-004.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/white-space/pre-wrap-tab-005.html.ini
@@ -0,0 +1,2 @@
+[pre-wrap-tab-005.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-text/white-space/pre-wrap-tab-006.html.ini
@@ -0,0 +1,2 @@
+[pre-wrap-tab-006.html]
+  expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom-view/scrollLeftTop.html.ini
@@ -0,0 +1,17 @@
+[scrollLeftTop.html]
+  [writing-mode:vertical-rl; direction:rtl]
+    expected:
+      if os == "android": FAIL
+
+  [writing-mode:horizontal-tb; direction:ltr]
+    expected:
+      if os == "android": FAIL
+
+  [writing-mode:horizontal-tb; direction:rtl]
+    expected:
+      if os == "android": FAIL
+
+  [writing-mode:vertical-lr; direction:ltr]
+    expected:
+      if os == "android": FAIL
+
--- a/testing/web-platform/meta/element-timing/images-repeated-resource.html.ini
+++ b/testing/web-platform/meta/element-timing/images-repeated-resource.html.ini
@@ -1,4 +1,7 @@
 [images-repeated-resource.html]
   [Element with elementtiming attribute is observable.]
     expected: FAIL
 
+  [Elements with elementtiming and same src are observable.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/event-timing/bufferbeforeonload.html.ini
+++ b/testing/web-platform/meta/event-timing/bufferbeforeonload.html.ini
@@ -1,5 +1,4 @@
 [bufferbeforeonload.html]
-  expected: TIMEOUT
   [Event Timing: click, onload.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/buffered-flag.html.ini
+++ b/testing/web-platform/meta/event-timing/buffered-flag.html.ini
@@ -1,5 +1,4 @@
 [buffered-flag.html]
-  expected: TIMEOUT
   [PerformanceObserver with buffered flag sees previous Event Timing entries]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/observethenonload.html.ini
+++ b/testing/web-platform/meta/event-timing/observethenonload.html.ini
@@ -1,5 +1,4 @@
 [observethenonload.html]
-  expected: TIMEOUT
   [Event Timing: click, observer, onload, click.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/onloadthenobserve-firstInput.html.ini
+++ b/testing/web-platform/meta/event-timing/onloadthenobserve-firstInput.html.ini
@@ -1,8 +1,7 @@
 [onloadthenobserve-firstInput.html]
-  expected: TIMEOUT
   [Event Timing: check firstInput after onload, observer, click, click.]
     expected: TIMEOUT
 
   [Event Timing: check first-input after onload, observer, click, click.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/onloadthenobserve.html.ini
+++ b/testing/web-platform/meta/event-timing/onloadthenobserve.html.ini
@@ -1,5 +1,4 @@
 [onloadthenobserve.html]
-  expected: TIMEOUT
   [Event Timing: onload, click, observer, click.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/programmatic-click-not-observed.html.ini
+++ b/testing/web-platform/meta/event-timing/programmatic-click-not-observed.html.ini
@@ -1,5 +1,4 @@
 [programmatic-click-not-observed.html]
-  expected: TIMEOUT
   [Event Timing: events from programmatic click are not observed]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/retrieve-firstInput.html.ini
+++ b/testing/web-platform/meta/event-timing/retrieve-firstInput.html.ini
@@ -1,8 +1,7 @@
 [retrieve-firstInput.html]
-  expected: TIMEOUT
   [Event Timing: check firstInput after onload, observer, click, click.]
     expected: TIMEOUT
 
   [Event Timing: check first-input after onload, observer, click, click.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/event-timing/timingconditions.html.ini
+++ b/testing/web-platform/meta/event-timing/timingconditions.html.ini
@@ -1,5 +1,4 @@
 [timingconditions.html]
-  expected: TIMEOUT
   [Event Timing only times certain types of trusted event.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/fetch/sec-metadata/redirect/redirect-http-upgrade.tentative.sub.html.ini
+++ b/testing/web-platform/meta/fetch/sec-metadata/redirect/redirect-http-upgrade.tentative.sub.html.ini
@@ -30,15 +30,13 @@
   [Http upgrade prefetch => No headers]
     disabled:
       if (os == "win") and debug and webrender: wpt-sync Bug 1565002
       if (os == "linux") and (processor == "x86_64") and not debug and webrender: wpt-sync Bug 1565002
       if (os == "linux") and (processor == "x86_64") and not debug and not webrender: wpt-sync Bug 1565002
       if (os == "linux") and (processor == "x86_64") and debug and webrender: wpt-sync Bug 1565002
       if (os == "linux") and (processor == "x86_64") and debug and not webrender and not sw-e10s: wpt-sync Bug 1565002
       if (os == "linux") and (processor == "x86_64") and debug and not webrender and sw-e10s: wpt-sync Bug 1565002
-    expected:
-      if not debug and (processor == "x86_64") and (os == "win") and not webrender: FAIL
-      FAIL
+    expected: FAIL
 
   [Http upgrade fetch() api => No headers]
     expected: FAIL
 
--- a/testing/web-platform/meta/fetch/sec-metadata/redirect/redirect-https-downgrade.tentative.sub.html.ini
+++ b/testing/web-platform/meta/fetch/sec-metadata/redirect/redirect-https-downgrade.tentative.sub.html.ini
@@ -2,22 +2,21 @@
   expected: TIMEOUT
   [Https downgrade track => No headers]
     expected: NOTRUN
 
   [Https downgrade font => No headers]
     expected: NOTRUN
 
   [Https downgrade prefetch => No headers]
+    bug: [https://bugzilla.mozilla.org/show_bug.cgi?id=1568717, https://bugzilla.mozilla.org/show_bug.cgi?id=1569098]
     expected:
-      if (os == "linux"): ["FAIL", "TIMEOUT", "PASS"]
-      if (os == "win"): ["FAIL", "TIMEOUT", "PASS"]
-      if (os == "mac"): ["FAIL", "TIMEOUT", "PASS"]
       if os == "android": PASS
+      if (os == "linux") and debug and webrender: TIMEOUT
+      if (os == "mac") and not debug: PASS
       FAIL
-    bug: [https://bugzilla.mozilla.org/show_bug.cgi?id=1568717, https://bugzilla.mozilla.org/show_bug.cgi?id=1569098]
 
   [Https downgrade stylesheet => No headers]
     expected: TIMEOUT
 
   [Https downgrade image => No headers]
     expected: NOTRUN
 
--- a/testing/web-platform/meta/html/cross-origin-opener/__dir__.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener/__dir__.ini
@@ -1,4 +1,4 @@
 prefs: [browser.tabs.remote.useCrossOriginOpenerPolicy:true]
 disabled:
   if verify: intermittent timeouts in verify mode
-leak-threshold: [default:51200]
+leak-threshold: [default:102400]
--- a/testing/web-platform/meta/html/cross-origin-opener/new_window_null.tentative.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener/new_window_null.tentative.html.ini
@@ -1,13 +1,13 @@
 [new_window_null.tentative.html]
   disabled:
     if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1559494
   expected:
-    if (os == "mac") and debug: ["OK", "TIMEOUT"]
+    if (os == "mac") and debug: [OK, TIMEOUT]
   [null document opening popup to http://not-web-platform.test:8000 with COOP: "same-origin"]
     expected:
-      if (os == "mac") and debug: ["PASS", "TIMEOUT"]
+      if (os == "mac") and debug: [PASS, TIMEOUT]
 
   [null document opening popup to http://www1.web-platform.test:8000 with COOP: "same-origin unsafe-allow-outgoing"]
     expected:
-      if (os == "mac") and debug: ["PASS", "TIMEOUT"]
+      if (os == "mac") and debug: [PASS, TIMEOUT]
 
--- a/testing/web-platform/meta/html/cross-origin-opener/new_window_same_origin.tentative.html.ini
+++ b/testing/web-platform/meta/html/cross-origin-opener/new_window_same_origin.tentative.html.ini
@@ -1,12 +1,11 @@
 [new_window_same_origin.tentative.html]
   expected:
-    if (os == "mac") and debug: ["OK", "ERROR"]
-    if (os == "linux") and sw-e10s: ["OK", "TIMEOUT"]
+    if (os == "linux") and sw-e10s: [OK, TIMEOUT]
   [same-origin document opening popup to http://www1.web-platform.test:8000 with COOP: "same-site unsafe-allow-outgoing"]
     expected:
       if os == "android": FAIL
 
   [same-origin document opening popup to http://web-platform.test:8000 with COOP: "same-origin unsafe-allow-outgoing"]
     expected:
       if os == "android": FAIL
 
@@ -20,20 +19,21 @@
 
   [same-origin document opening popup to http://web-platform.test:8000 with COOP: "same-site unsafe-allow-outgoing"]
     expected:
       if os == "android": FAIL
 
   [same-origin document opening popup to http://not-web-platform.test:8000 with COOP: "same-site"]
     expected:
       if os == "android": FAIL
+      if (os == "mac") and debug: PASS
 
   [same-origin document opening popup to http://not-web-platform.test:8000 with COOP: "same-site unsafe-allow-outgoing"]
     expected:
-      if (os == "mac") and debug: ["PASS", "TIMEOUT"]
+      if (os == "mac") and debug: [PASS, TIMEOUT]
       if os == "android": FAIL
 
   [same-origin document opening popup to http://www1.web-platform.test:8000 with COOP: "same-site"]
     expected:
       if os == "android": FAIL
 
   [same-origin document opening popup to http://www1.web-platform.test:8000 with COOP: ""]
     expected:
@@ -64,11 +64,11 @@
       if os == "android": FAIL
 
   [same-origin document opening popup to http://web-platform.test:8000 with COOP: "jibberish"]
     expected:
       if os == "android": FAIL
 
   [same-origin document opening popup to http://not-web-platform.test:8000 with COOP: "same-origin"]
     expected:
-      if (os == "linux") and sw-e10s: ["PASS", "TIMEOUT"]
+      if (os == "linux") and sw-e10s: [PASS, TIMEOUT]
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/html/dom/interfaces.https.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.https.html.ini
@@ -1126,8 +1126,95 @@ prefs: [dom.security.featurePolicy.enabl
     expected: FAIL
 
   [ElementInternals interface: attribute validationMessage]
     expected: FAIL
 
   [ElementInternals interface: attribute validity]
     expected: FAIL
 
+  [SVGSVGElement interface: attribute onstorage]
+    expected: FAIL
+
+  [SVGAElement interface: attribute hash]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onbeforeprint]
+    expected: FAIL
+
+  [SVGAElement interface: attribute protocol]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onoffline]
+    expected: FAIL
+
+  [SVGAElement interface: stringifier]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onpageshow]
+    expected: FAIL
+
+  [SVGAElement interface: attribute search]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onunload]
+    expected: FAIL
+
+  [SVGAElement interface: attribute port]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onpopstate]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onmessageerror]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onmessage]
+    expected: FAIL
+
+  [SVGAElement interface: attribute hostname]
+    expected: FAIL
+
+  [SVGAElement interface: attribute pathname]
+    expected: FAIL
+
+  [SVGAElement interface: attribute host]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onlanguagechange]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onunhandledrejection]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onrejectionhandled]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onhashchange]
+    expected: FAIL
+
+  [SVGAElement interface: attribute origin]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onbeforeunload]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onafterprint]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute ononline]
+    expected: FAIL
+
+  [SVGAElement interface: attribute href]
+    expected: FAIL
+
+  [SVGSVGElement interface: attribute onpagehide]
+    expected: FAIL
+
+  [SVGAElement interface: attribute password]
+    expected: FAIL
+
+  [SVGAElement interface: attribute username]
+    expected: FAIL
+
+  [SVGElement interface: attribute onformdata]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[broadcastchannel-success.html]
-  [Structured cloning of SharedArrayBuffers: BroadcastChannel within the same agent cluster]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html.ini
@@ -0,0 +1,4 @@
+[broadcastchannel-success.https.html]
+  [Structured cloning of SharedArrayBuffers: BroadcastChannel within the same agent cluster]
+    expected: FAIL
+
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-webvtt-two-cue-layout-after-first-end.html.ini
@@ -1,3 +1,3 @@
 [track-webvtt-two-cue-layout-after-first-end.html]
   disabled:
-    if (os == "android"): https://bugzilla.mozilla.org/show_bug.cgi?id=1536762
+    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1536762
--- a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html.ini
@@ -1,17 +1,13 @@
 [viewport-change.html]
   [picture: source (max-width:500px) broken image, img valid image, resize to narrow]
-    expected:
-      FAIL
+    expected: FAIL
 
   [picture: source (max-width:500px) broken image, img broken image, resize to narrow]
-    expected:
-      FAIL
+    expected: FAIL
 
   [picture: source (max-width:500px) broken image, img broken image, resize to wide]
-    expected:
-      FAIL
+    expected: FAIL
 
   [picture: source (max-width:500px) valid image, img broken image, resize to wide]
-    expected:
-      FAIL
+    expected: FAIL
 
--- a/testing/web-platform/meta/imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html.ini
+++ b/testing/web-platform/meta/imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html.ini
@@ -1,4 +1,7 @@
 [tranferFromImageBitmap-ToBlob-offscreen.html]
   [Test that transferToBlob works and produce the expected image]
     expected: FAIL
 
+  [Test that convertToBlob works and produce the expected image]
+    expected: FAIL
+
--- a/testing/web-platform/meta/largest-contentful-paint/cross-origin-image.sub.html.ini
+++ b/testing/web-platform/meta/largest-contentful-paint/cross-origin-image.sub.html.ini
@@ -1,7 +1,6 @@
 [cross-origin-image.sub.html]
   [Cross-origin image is observable, with startTime equal to 0.]
     expected: FAIL
 
   [Cross-origin image is observable, with renderTime equal to 0.]
     expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/largest-contentful-paint/loadTime-after-appendChild.html.ini
@@ -0,0 +1,4 @@
+[loadTime-after-appendChild.html]
+  [Image loadTime occurs after appendChild is called.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/largest-contentful-paint/repeated-image.html.ini
@@ -0,0 +1,4 @@
+[repeated-image.html]
+  [Repeated image produces different timestamps.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/largest-contentful-paint/supported-lcp-type.html.ini
@@ -0,0 +1,7 @@
+[supported-lcp-type.html]
+  [supportedEntryTypes contains 'layoutShift'.]
+    expected: FAIL
+
+  [supportedEntryTypes contains 'largest-contentful-paint'.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/layout-instability/buffer-layout-shift.html.ini
+++ b/testing/web-platform/meta/layout-instability/buffer-layout-shift.html.ini
@@ -1,8 +1,7 @@
 [buffer-layout-shift.html]
-  expected: TIMEOUT
   [Layout shift before onload is buffered into the performance timeline.]
     expected: TIMEOUT
 
   [Layout shift before onload is not buffered into the performance timeline.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/layout-instability/buffered-flag.html.ini
+++ b/testing/web-platform/meta/layout-instability/buffered-flag.html.ini
@@ -1,5 +1,4 @@
 [buffered-flag.html]
-  expected: TIMEOUT
   [PerformanceObserver with buffered flag sees previous layout-shift entry.]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/meta/layout-instability/observe-layout-shift.html.ini
+++ b/testing/web-platform/meta/layout-instability/observe-layout-shift.html.ini
@@ -1,8 +1,8 @@
 [observe-layout-shift.html]
   expected: TIMEOUT
   [Layout shift is observable via PerformanceObserver.]
-    expected: TIMEOUT
+    expected: FAIL
 
   [Layout shift within user input is observable via PerformanceObserver.]
     expected: TIMEOUT
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/loading/lazyload/picture-loading-lazy.tentative.html.ini
@@ -0,0 +1,10 @@
+[picture-loading-lazy.tentative.html]
+  [Picture elements with loading='lazy' load when in the viewport]
+    expected: FAIL
+
+  [Images with loading='lazy' in picture elements load when near the viewport]
+    expected: FAIL
+
+  [Test that the loading=lazy <picture> element below viewport was deferred, on document load.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/longtask-timing/buffered-flag-unsupported.window.js.ini
@@ -0,0 +1,4 @@
+[buffered-flag-unsupported.window.html]
+  [PerformanceObserver with buffered flag cannot see previous longtask entries.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/longtask-timing/idlharness.window.js.ini
+++ b/testing/web-platform/meta/longtask-timing/idlharness.window.js.ini
@@ -48,11 +48,17 @@
     expected: FAIL
 
   [TaskAttributionTiming interface: attribute containerId]
     expected: FAIL
 
   [TaskAttributionTiming interface: attribute containerName]
     expected: FAIL
 
+  [TaskAttributionTiming interface: operation toJSON()]
+    expected: FAIL
+
   [idl_test setup]
     expected: FAIL
 
+  [PerformanceLongTaskTiming interface: operation toJSON()]
+    expected: FAIL
+
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,2 +1,2 @@
-local: 3b0184c6df9a09794a41b4164c400f0e68ba15d5
-upstream: 31a0954670ab6d49cf28f013dbde854942634067
+local: 350c9eedae9cde55812d238fca4307ac9a98a066
+upstream: 7896f18d8a953f518536307b4c05dae63dccf87e
--- a/testing/web-platform/meta/notifications/__dir__.ini
+++ b/testing/web-platform/meta/notifications/__dir__.ini
@@ -1,1 +1,2 @@
 lsan-allowed: [Alloc, NS_GetXPTCallStub, NewPage, nsXPCWrappedJS::GetNewOrUsed]
+leak-threshold: [default:51200]
--- a/testing/web-platform/meta/performance-timeline/case-sensitivity.any.js.ini
+++ b/testing/web-platform/meta/performance-timeline/case-sensitivity.any.js.ini
@@ -1,7 +1,7 @@
 [case-sensitivity.any.html]
   [getEntriesByName values are case sensitive]
     expected:
-      if webrender and (os == "linux") and not debug: ["PASS", "FAIL"]
+      if webrender and (os == "linux") and not debug: [PASS, FAIL]
 
 
 [case-sensitivity.any.worker.html]
--- a/testing/web-platform/meta/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html.ini
+++ b/testing/web-platform/meta/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html.ini
@@ -1,6 +1,6 @@
 [pointerevent_movementxy_with_pointerlock.html]
   [mouse pointerevent movementX/Y with pointerlock test]
     expected:
       if os == "android": FAIL
-      [PASS, FAIL]
+      if (os == "win") and debug and webrender: FAIL
 
--- a/testing/web-platform/meta/service-workers/service-worker/interfaces-sw.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/interfaces-sw.https.html.ini
@@ -90,8 +90,11 @@
     expected: FAIL
 
   [ServiceWorkerGlobalScope interface: self must inherit property "serviceWorker" with the proper type]
     expected: FAIL
 
   [ServiceWorkerGlobalScope interface: attribute serviceWorker]
     expected: FAIL
 
+  [test setup (cache creation)]
+    expected: FAIL
+
--- a/testing/web-platform/meta/service-workers/service-worker/update.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/update.https.html.ini
@@ -1,7 +1,7 @@
 [update.https.html]
-  expected: ERROR
   [Update a registration.]
     expected:
       if sw-e10s: TIMEOUT
-      FAIL
 
+  [update() should fail when a response for the main script is redirect.]
+    expected: FAIL
--- a/testing/web-platform/meta/sms/interceptor.https.html.ini
+++ b/testing/web-platform/meta/sms/interceptor.https.html.ini
@@ -22,8 +22,11 @@
     expected: NOTRUN
 
   [Should throw error with invalid timeout (null)]
     expected: NOTRUN
 
   [Should throw error with invalid timeout (-1)]
     expected: NOTRUN
 
+  [Deal with cancelled requests]
+    expected: NOTRUN
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/std-toast/reflection.html.ini
@@ -0,0 +1,2 @@
+[reflection.html]
+  expected: TIMEOUT
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/web-nfc/NDEFMessage_constructor.https.html.ini
@@ -0,0 +1,10 @@
+[NDEFMessage_constructor.https.html]
+  [NDEFMessage constructor with a text record]
+    expected: FAIL
+
+  [NDEFMessage constructor with null init dict]
+    expected: FAIL
+
+  [NDEFMessage constructor without init dict]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/web-nfc/NDEFRecord_constructor.https.html.ini
@@ -0,0 +1,13 @@
+[NDEFRecord_constructor.https.html]
+  [NDEFRecord constructor with opaque data]
+    expected: FAIL
+
+  [NDEFRecord constructor with json data]
+    expected: FAIL
+
+  [NDEFRecord constructor without init dict]
+    expected: FAIL
+
+  [NDEFRecord constructor with null init dict]
+    expected: FAIL
+
--- a/testing/web-platform/meta/web-nfc/NFCReadingEvent_constructor.https.html.ini
+++ b/testing/web-platform/meta/web-nfc/NFCReadingEvent_constructor.https.html.ini
@@ -1,7 +1,16 @@
 [NFCReadingEvent_constructor.https.html]
   [NFCReadingEvent constructor without init dict]
     expected: FAIL
 
+  [NFCReadingEvent constructor with serialNumber not present]
+    expected: FAIL
+
+  [NFCReadingEvent constructor with null serialNumber]
+    expected: FAIL
+
+  [NFCReadingEvent constructor with null message]
+    expected: FAIL
+
   [NFCReadingEvent constructor with valid parameters]
     expected: FAIL
 
--- a/testing/web-platform/meta/webrtc/idlharness.https.window.js.ini
+++ b/testing/web-platform/meta/webrtc/idlharness.https.window.js.ini
@@ -573,8 +573,14 @@
     expected: FAIL
 
   [RTCError interface: existence and properties of interface prototype object]
     expected: FAIL
 
   [RTCError interface: existence and properties of interface object]
     expected: FAIL
 
+  [RTCDataChannel interface: new RTCPeerConnection().createDataChannel('') must inherit property "onclosing" with the proper type]
+    expected: FAIL
+
+  [RTCDataChannel interface: attribute onclosing]
+    expected: FAIL
+
--- a/testing/web-platform/meta/webxr/events_input_source_recreation.https.html.ini
+++ b/testing/web-platform/meta/webxr/events_input_source_recreation.https.html.ini
@@ -1,4 +1,5 @@
 [events_input_source_recreation.https.html]
+  expected: ERROR
   [Input sources are re-created when handedness or target ray mode changes]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/events_input_sources_change.https.html.ini
+++ b/testing/web-platform/meta/webxr/events_input_sources_change.https.html.ini
@@ -1,4 +1,5 @@
 [events_input_sources_change.https.html]
+  expected: ERROR
   [Transient input sources fire events in the right order]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/events_referenceSpace_reset.https.html.ini
+++ b/testing/web-platform/meta/webxr/events_referenceSpace_reset.https.html.ini
@@ -1,7 +1,8 @@
 [events_referenceSpace_reset.https.html]
+  expected: ERROR
   [XRSession resetpose from a device properly fires off the right events for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRSession resetpose from a device properly fires off the right events for immersive sessions]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/events_session_select.https.html.ini
+++ b/testing/web-platform/meta/webxr/events_session_select.https.html.ini
@@ -1,4 +1,5 @@
 [events_session_select.https.html]
+  expected: ERROR
   [XRInputSources primary input presses properly fires off the right events]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/events_session_select_subframe.https.html.ini
+++ b/testing/web-platform/meta/webxr/events_session_select_subframe.https.html.ini
@@ -1,4 +1,5 @@
 [events_session_select_subframe.https.html]
+  expected: ERROR
   [Ensures that an XRInputSources primary input being pressed and released in the space of a single frame properly fires off the right events]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/getInputPose_handedness.https.html.ini
+++ b/testing/web-platform/meta/webxr/getInputPose_handedness.https.html.ini
@@ -1,4 +1,5 @@
 [getInputPose_handedness.https.html]
+  expected: ERROR
   [XRInputSources properly communicate their handedness]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/getInputPose_pointer.https.html.ini
+++ b/testing/web-platform/meta/webxr/getInputPose_pointer.https.html.ini
@@ -1,4 +1,5 @@
 [getInputPose_pointer.https.html]
+  expected: ERROR
   [XRInputSources with a target ray mode of 'tracked-pointer' properly communicate their poses]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrBoundedReferenceSpace_updates.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrBoundedReferenceSpace_updates.https.html.ini
@@ -1,4 +1,5 @@
 [xrBoundedReferenceSpace_updates.https.html]
+  expected: ERROR
   ['XRBoundedReferenceSpace updates properly when the changes are applied]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrDevice_disconnect_ends.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrDevice_disconnect_ends.https.html.ini
@@ -1,4 +1,5 @@
 [xrDevice_disconnect_ends.https.html]
+  expected: ERROR
   [Immersive session ends when device is disconnected]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrDevice_requestSession_immersive.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrDevice_requestSession_immersive.https.html.ini
@@ -1,4 +1,11 @@
 [xrDevice_requestSession_immersive.https.html]
+  expected: ERROR
   [Tests requestSession resolves when supported]
     expected: FAIL
 
+  [Tests requestSession ignores unknown optionalFeatures]
+    expected: NOTRUN
+
+  [Tests requestSession accepts XRSessionInit dictionary]
+    expected: NOTRUN
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webxr/xrDevice_requestSession_optionalFeatures.https.html.ini
@@ -0,0 +1,11 @@
+[xrDevice_requestSession_optionalFeatures.https.html]
+  expected: ERROR
+  [Tests requestSession ignores unknown optionalFeatures]
+    expected: NOTRUN
+
+  [Tests requestSession accepts XRSessionInit dictionary]
+    expected: FAIL
+
+  [Tests requestSession accepts XRSessionInit dictionary with empty feature lists]
+    expected: NOTRUN
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webxr/xrDevice_requestSession_requiredFeatures_unknown.https.html.ini
@@ -0,0 +1,4 @@
+[xrDevice_requestSession_requiredFeatures_unknown.https.html]
+  [Tests requestSession rejects for unknown requiredFeatures]
+    expected: FAIL
+
--- a/testing/web-platform/meta/webxr/xrFrame_getPose.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrFrame_getPose.https.html.ini
@@ -1,7 +1,8 @@
 [xrFrame_getPose.https.html]
+  expected: ERROR
   [XRFrame.getPose works for immersive sessions]
     expected: FAIL
 
   [XRFrame.getPose works for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
--- a/testing/web-platform/meta/webxr/xrFrame_lifetime.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrFrame_lifetime.https.html.ini
@@ -1,7 +1,8 @@
 [xrFrame_lifetime.https.html]
+  expected: ERROR
   [XRFrame methods throw exceptions outside of the requestAnimationFrame callback for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRFrame methods throw exceptions outside of the requestAnimationFrame callback for immersive sessions]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrInputSource_add_remove.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrInputSource_add_remove.https.html.ini
@@ -1,4 +1,5 @@
 [xrInputSource_add_remove.https.html]
+  expected: ERROR
   [XRInputSources can be properly added and removed from the session]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrInputSource_gamepad_disconnect.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrInputSource_gamepad_disconnect.https.html.ini
@@ -1,4 +1,5 @@
 [xrInputSource_gamepad_disconnect.https.html]
+  expected: ERROR
   [WebXR InputSource's gamepad gets disconnected when the input source is removed]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrInputSource_gamepad_input_registered.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrInputSource_gamepad_input_registered.https.html.ini
@@ -1,4 +1,5 @@
 [xrInputSource_gamepad_input_registered.https.html]
+  expected: ERROR
   [WebXR InputSource's gamepad properly registers input]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrReferenceSpace_originOffset.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrReferenceSpace_originOffset.https.html.ini
@@ -1,4 +1,5 @@
 [xrReferenceSpace_originOffset.https.html]
+  expected: ERROR
   [Updating XRReferenceSpace origin offset updates view and input matrices.]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrReferenceSpace_originOffsetBounded.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrReferenceSpace_originOffsetBounded.https.html.ini
@@ -1,4 +1,5 @@
 [xrReferenceSpace_originOffsetBounded.https.html]
+  expected: ERROR
   [Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry.]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrRigidTransform_constructor.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrRigidTransform_constructor.https.html.ini
@@ -1,4 +1,5 @@
 [xrRigidTransform_constructor.https.html]
+  expected: ERROR
   [XRRigidTransform constructor works]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrRigidTransform_inverse.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrRigidTransform_inverse.https.html.ini
@@ -1,4 +1,5 @@
 [xrRigidTransform_inverse.https.html]
+  expected: ERROR
   [XRRigidTransform inverse works]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_cancelAnimationFrame.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_cancelAnimationFrame.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_cancelAnimationFrame.https.html]
+  expected: ERROR
   [XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRSession requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame for immersive sessions]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_cancelAnimationFrame_invalidhandle.https.html]
+  expected: ERROR
   [XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on immersive testSession]
     expected: FAIL
 
   [XRSession cancelAnimationFrame does not have unexpected behavior when given invalid handles on non-immersive testSession]
-    expected: FAIL
+    expected: NOTRUN
 
--- a/testing/web-platform/meta/webxr/xrSession_end.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_end.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_end.https.html]
+  expected: ERROR
   [end event fires when non-immersive session ends]
-    expected: FAIL
+    expected: NOTRUN
 
   [end event fires when immersive session ends]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_input_events_end.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_input_events_end.https.html.ini
@@ -1,4 +1,5 @@
 [xrSession_input_events_end.https.html]
+  expected: ERROR
   [Calling end during an input callback stops processing at the right time]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_callback_calls.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_callback_calls.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_requestAnimationFrame_callback_calls.https.html]
+  expected: ERROR
   [XRSession requestAnimationFrame calls the provided callback a non-immersive session]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRSession requestAnimationFrame calls the provided callback for an immersive session]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_data_valid.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_data_valid.https.html.ini
@@ -1,4 +1,5 @@
 [xrSession_requestAnimationFrame_data_valid.https.html]
+  expected: ERROR
   [RequestAnimationFrame resolves with good data]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_requestAnimationFrame_getViewerPose.https.html]
+  expected: ERROR
   [XRFrame getViewerPose updates on the next frame for non-immersive sessions]
     expected: FAIL
 
   [XRFrame getViewerPose updates on the next frame for immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
--- a/testing/web-platform/meta/webxr/xrSession_requestReferenceSpace.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_requestReferenceSpace.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_requestReferenceSpace.https.html]
+  expected: ERROR
   [Non-immersive XRSession requestReferenceSpace returns expected objects]
-    expected: FAIL
+    expected: NOTRUN
 
   [Immersive XRSession requestReferenceSpace returns expected objects]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webxr/xrSession_requestReferenceSpace_features.https.html.ini
@@ -0,0 +1,38 @@
+[xrSession_requestReferenceSpace_features.https.html]
+  expected: ERROR
+  [Non-immersive session rejects unbounded space even when requested]
+    expected: NOTRUN
+
+  [Immersive session supports local space by default]
+    expected: NOTRUN
+
+  [Non-immersive session supports local-floor space when required]
+    expected: NOTRUN
+
+  [Immersive session rejects local-floor space if not requested]
+    expected: NOTRUN
+
+  [Immersive session supports local-floor space when required]
+    expected: NOTRUN
+
+  [Non-immersive session rejects bounded-floor space even when requested]
+    expected: NOTRUN
+
+  [Non-immersive session supports local space when optional]
+    expected: NOTRUN
+
+  [Immersive session supports local-floor space when optional]
+    expected: NOTRUN
+
+  [Non-immersive session supports local space when required]
+    expected: NOTRUN
+
+  [Non-immersive session supports viewer space by default]
+    expected: FAIL
+
+  [Non-immersive session rejects local space if not requested]
+    expected: NOTRUN
+
+  [Immersive session supports viewer space by default]
+    expected: NOTRUN
+
--- a/testing/web-platform/meta/webxr/xrSession_viewer_referenceSpace.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrSession_viewer_referenceSpace.https.html.ini
@@ -1,7 +1,8 @@
 [xrSession_viewer_referenceSpace.https.html]
+  expected: ERROR
   [Identity reference space provides correct poses for immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [Identity reference space provides correct poses for inline sessions]
     expected: FAIL
 
--- a/testing/web-platform/meta/webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html.ini
@@ -1,7 +1,8 @@
 [xrStationaryReferenceSpace_floorlevel_updates.https.html]
+  expected: ERROR
   ['floor-level' XRStationaryReferenceSpace updates properly when the transform changes for immersive sessions]
     expected: FAIL
 
   ['floor-level' XRStationaryReferenceSpace updates properly when the transform changes for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
--- a/testing/web-platform/meta/webxr/xrView_eyes.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrView_eyes.https.html.ini
@@ -1,7 +1,8 @@
 [xrView_eyes.https.html]
+  expected: ERROR
   [XRView.eye is correct for non-immersive sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRView.eye is correct for immersive sessions]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webxr/xrWebGLLayer_framebuffer.https.html.ini
@@ -0,0 +1,8 @@
+[xrWebGLLayer_framebuffer.https.html]
+  expected: ERROR
+  [XRWebGLLayer reports a valid framebuffer for inline sessions]
+    expected: NOTRUN
+
+  [XRWebGLLayer reports a valid framebuffer for immersive sessions]
+    expected: FAIL
+
--- a/testing/web-platform/meta/webxr/xrWebGLLayer_viewports.https.html.ini
+++ b/testing/web-platform/meta/webxr/xrWebGLLayer_viewports.https.html.ini
@@ -1,7 +1,8 @@
 [xrWebGLLayer_viewports.https.html]
+  expected: ERROR
   [XRWebGLLayer reports a valid viewports for inline sessions]
-    expected: FAIL
+    expected: NOTRUN
 
   [XRWebGLLayer reports a valid viewports for immersive sessions]
     expected: FAIL
 
--- a/testing/web-platform/tests/animation-worklet/common.js
+++ b/testing/web-platform/tests/animation-worklet/common.js
@@ -13,23 +13,30 @@ function registerPassthroughAnimator() {
 function registerConstantLocalTimeAnimator(localTime) {
   return runInAnimationWorklet(`
     registerAnimator('constant_time', class {
       animate(currentTime, effect) { effect.localTime = ${localTime}; }
     });
   `);
 }
 
-
 function runInAnimationWorklet(code) {
   return CSS.animationWorklet.addModule(
     URL.createObjectURL(new Blob([code], {type: 'text/javascript'}))
   );
 }
 
+function approxEquals(actual, expected){
+  // precision in ms
+  const epsilon = 0.005;
+  const lowerBound = (expected - epsilon) < actual;
+  const upperBound = (expected + epsilon) > actual;
+  return lowerBound && upperBound;
+}
+
 function waitForAsyncAnimationFrames(count) {
   // In Chrome, waiting for N+1 main thread frames guarantees that compositor has produced
   // at least N frames.
   // TODO(majidvp): re-evaluate this choice once other browsers have implemented
   // AnimationWorklet.
   return waitForAnimationFrames(count + 1);
 }
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/animation-worklet/worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html
@@ -0,0 +1,87 @@
+<html>
+<title>Animation Worklet should update calculated timing whenever localTime changes</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+
+<div id="box"></div>
+
+<script id="get_computed_timing_animator" type="text/worklet">
+  registerAnimator('get_computed_timing', class {
+    constructor(options, state) {
+      this.step = state ? state.step : 0;
+    }
+    state() {
+      return {
+        step: 0
+      }
+    }
+    animate(currentTime, effect){
+      if (this.step === 0){
+        // check calculated timing values before ever setting effect.localTime
+        effect.localTime = (effect.getComputedTiming().currentIteration * 100) + (effect.getComputedTiming().progress * 100);
+        this.step = 1;
+      }
+      else if (this.step === 1){
+        // set effect.localTime, this should be the first time calculated timing values are computed
+        effect.localTime = 420; // 20% of the way through the last iteration
+
+        // using the calculated timing of effect, set effect.localTime.
+        effect.localTime = (effect.getComputedTiming().currentIteration * 100) + (effect.getComputedTiming().progress * 100);
+        this.step = 2;
+      }
+      else if (this.step === 2){
+        // set effect.localTime to null
+        effect.localTime = null;
+        effect.localTime = (effect.getComputedTiming().currentIteration * 100) + (effect.getComputedTiming().progress * 100);
+        this.step = 3;
+      }
+      else if (this.step === 3){
+        // Check to make sure we can go from null to a valid localTime and that calculated timing values are computed
+        effect.localTime = 350; // 50% of the way through second iteration
+        effect.localTime = (effect.getComputedTiming().currentIteration * 100) + (effect.getComputedTiming().progress * 100);
+        this.step = 4;
+      }
+    }
+  });
+</script>
+
+<script>
+  promise_test(async t => {
+    await runInAnimationWorklet(document.getElementById('get_computed_timing_animator').textContent);
+
+    const box = document.getElementById("box");
+    const effect = new KeyframeEffect(
+      box,
+      [
+        { opacity: 0 },
+        { opacity: 1 }
+      ], {
+        delay: 200,
+        duration: 100,
+        iterations: 3
+      }
+    );
+
+    const animation = new WorkletAnimation('get_computed_timing', effect);
+    animation.play();
+
+    // check calculated timing values before ever setting effect.localTime
+    await waitForAnimationFrameWithCondition(() => {return approxEquals(effect.getComputedTiming().localTime, 0)});
+
+    // Check to make sure initial values can be set for computed timing
+    await waitForAnimationFrameWithCondition(() => {return approxEquals(effect.getComputedTiming().localTime, 220)});
+
+    // Make sure setting effect.localTime to null causes calculated timing values to be computed
+    await waitForAnimationFrameWithCondition(() => {return approxEquals(effect.getComputedTiming().localTime, 0)});
+
+    // Make sure we can go from null to a valid localTime and that calculated timing values are computed
+    await waitForAnimationFrameWithCondition(() => {return approxEquals(effect.getComputedTiming().localTime, 150)});
+
+    // Passes if it doesn't timeout
+    animation.cancel();
+  }, "WorkletAnimation effect should recompute its calculated timing if its local time changes");
+</script>
\ No newline at end of file
--- a/testing/web-platform/tests/cookie-store/cookieListItem_attributes.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieListItem_attributes.tentative.https.window.js
@@ -1,229 +1,226 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 const kCurrentHostname = (new URL(self.location.href)).hostname;
 
 const kOneDay = 24 * 60 * 60 * 1000;
 const kTenYears = 10 * 365 * kOneDay;
 const kTenYearsFromNow = Date.now() + kTenYears;
 
 const kCookieListItemKeys =
     ['domain', 'expires', 'name', 'path', 'sameSite', 'secure', 'value'].sort();
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_equals(cookie.expires, null);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set defaults with positional name and value');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set({ name: 'cookie-name', value: 'cookie-value' });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_equals(cookie.expires, null);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set defaults with name and value in options');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value',
                         { expires: kTenYearsFromNow });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_approx_equals(cookie.expires, kTenYearsFromNow, kOneDay);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set with expires set to a timestamp 10 ' +
    'years in the future');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set({ name: 'cookie-name', value: 'cookie-value',
                           expires: kTenYearsFromNow });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_approx_equals(cookie.expires, kTenYearsFromNow, kOneDay);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set with name and value in options and ' +
    'expires set to a future timestamp');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value',
                         { expires: new Date(kTenYearsFromNow) });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_approx_equals(cookie.expires, kTenYearsFromNow, kOneDay);
   assert_equals(cookie.secure, true);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set with expires set to a Date 10 ' +
    'years in the future');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set({ name: 'cookie-name', value: 'cookie-value',
                           expires: new Date(kTenYearsFromNow) });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_approx_equals(cookie.expires, kTenYearsFromNow, kOneDay);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set with name and value in options and ' +
    'expires set to a future Date');
 
 promise_test(async testCase => {
   await cookieStore.delete({ name: 'cookie-name', domain: kCurrentHostname });
 
   await cookieStore.set('cookie-name', 'cookie-value',
                         { domain: kCurrentHostname });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: kCurrentHostname });
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, kCurrentHostname);
   assert_equals(cookie.path, '/');
   assert_equals(cookie.expires, null);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', domain: kCurrentHostname });
-  });
 }, 'CookieListItem - cookieStore.set with domain set to the current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentPath = currentUrl.pathname;
   const currentDirectory =
       currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
   await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
 
   await cookieStore.set('cookie-name', 'cookie-value',
                         { path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, currentDirectory);
   assert_equals(cookie.expires, null);
   assert_equals(cookie.secure, true);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
-  });
 }, 'CookieListItem - cookieStore.set with path set to the current directory');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value', { secure: false });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
   assert_equals(cookie.domain, null);
   assert_equals(cookie.path, '/');
   assert_equals(cookie.expires, null);
   assert_equals(cookie.secure, false);
   assert_equals(cookie.sameSite, 'strict');
   assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'CookieListItem - cookieStore.set with secure set to false');
 
 ['strict', 'lax', 'unrestricted'].forEach(sameSiteValue => {
   promise_test(async testCase => {
     await cookieStore.delete('cookie-name');
 
     await cookieStore.set({
         name: 'cookie-name', value: 'cookie-value', sameSite: sameSiteValue });
+    testCase.add_cleanup(async () => {
+      await cookieStore.delete('cookie-name');
+    });
     const cookie = await cookieStore.get('cookie-name');
     assert_equals(cookie.name, 'cookie-name');
     assert_equals(cookie.value, 'cookie-value');
     assert_equals(cookie.domain, null);
     assert_equals(cookie.path, '/');
     assert_equals(cookie.expires, null);
     assert_equals(cookie.secure, true);
     assert_equals(cookie.sameSite, sameSiteValue);
     assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-    await async_cleanup(() => cookieStore.delete('cookie-name'));
   }, `CookieListItem - cookieStore.set with sameSite set to ${sameSiteValue}`);
 
   promise_test(async testCase => {
     await cookieStore.delete('cookie-name');
 
     await cookieStore.set('cookie-name', 'cookie-value',
                           { sameSite: sameSiteValue });
+    testCase.add_cleanup(async () => {
+      await cookieStore.delete('cookie-name');
+    });
     const cookie = await cookieStore.get('cookie-name');
     assert_equals(cookie.name, 'cookie-name');
     assert_equals(cookie.value, 'cookie-value');
     assert_equals(cookie.domain, null);
     assert_equals(cookie.path, '/');
     assert_equals(cookie.expires, null);
     assert_equals(cookie.secure, true);
     assert_equals(cookie.sameSite, sameSiteValue);
     assert_array_equals(Object.keys(cookie).sort(), kCookieListItemKeys);
-
-    await async_cleanup(() => cookieStore.delete('cookie-name'));
   }, 'CookieListItem - cookieStore.set with positional name and value and ' +
      `sameSite set to ${sameSiteValue}`);
-});
+});
\ No newline at end of file
--- a/testing/web-platform/tests/cookie-store/cookieStore_delete_arguments.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_delete_arguments.tentative.https.window.js
@@ -1,61 +1,52 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
 
   await cookieStore.delete('cookie-name');
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
 }, 'cookieStore.delete with positional name');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   await cookieStore.delete({ name: 'cookie-name' });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.delete with name in options');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   await cookieStore.delete('cookie-name', { name: 'wrong-cookie-name' });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.delete with name in both positional arguments and options');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   await cookieStore.set(
       'cookie-name', 'cookie-value', { domain: currentDomain });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
+  });
 
   await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
-  });
 }, 'cookieStore.delete with domain set to the current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   const subDomain = `sub.${currentDomain}`;
 
   await promise_rejects(testCase, new TypeError(), cookieStore.delete(
@@ -74,24 +65,23 @@ promise_test(async testCase => {
 }, 'cookieStore.delete with domain set to a non-domain-matching suffix of ' +
    'the current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   await cookieStore.set(
       'cookie-name', 'cookie-value', { domain: currentDomain });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
+  });
 
   await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
-  });
 }, 'cookieStore.delete with name in options and domain set to the current ' +
    'hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   const subDomain = `sub.${currentDomain}`;
 
@@ -114,50 +104,49 @@ promise_test(async testCase => {
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentPath = currentUrl.pathname;
   const currentDirectory =
       currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
   await cookieStore.set(
       'cookie-name', 'cookie-value', { path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
 
   await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
-  });
 }, 'cookieStore.delete with path set to the current directory');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentPath = currentUrl.pathname;
   const currentDirectory =
       currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
   const subDirectory = currentDirectory + "subdir/";
   await cookieStore.set(
       'cookie-name', 'cookie-value', { path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
 
   await cookieStore.delete({ name: 'cookie-name', path: subDirectory });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
-  });
 }, 'cookieStore.delete with path set to subdirectory of the current directory');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie_attributes = await cookieStore.get('cookie-name');
   assert_equals(cookie_attributes.name, 'cookie-name');
   assert_equals(cookie_attributes.value, 'cookie-value');
 
   await cookieStore.delete(cookie_attributes);
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.delete with get result');
--- a/testing/web-platform/tests/cookie-store/cookieStore_event_basic.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_event_basic.tentative.https.window.js
@@ -1,29 +1,20 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   const eventPromise = new Promise((resolve) => {
     cookieStore.onchange = resolve;
   });
 
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const event = await eventPromise;
   assert_true(event instanceof CookieChangeEvent);
   assert_equals(event.type, 'change');
   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);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore fires change event for cookie set by cookieStore.set()');
--- a/testing/web-platform/tests/cookie-store/cookieStore_event_delete.tenative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_event_delete.tenative.https.window.js
@@ -1,31 +1,22 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const eventPromise = new Promise((resolve) => {
     cookieStore.onchange = resolve;
   });
   await cookieStore.delete('cookie-name');
   const event = await eventPromise;
   assert_true(event instanceof CookieChangeEvent);
   assert_equals(event.type, 'change');
   assert_equals(event.deleted.length, 1);
   assert_equals(event.deleted[0].name, 'cookie-name');
   assert_equals(
       event.deleted[0].value, undefined,
       'Cookie change events for deletions should not have cookie values');
   assert_equals(event.changed.length, 0);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore fires change event for cookie deleted by cookieStore.delete()');
\ No newline at end of file
--- a/testing/web-platform/tests/cookie-store/cookieStore_event_overwrite.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_event_overwrite.tentative.https.window.js
@@ -1,31 +1,22 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const eventPromise = new Promise((resolve) => {
     cookieStore.onchange = resolve;
   });
 
   await cookieStore.set('cookie-name', 'new-cookie-value');
 
   const event = await eventPromise;
   assert_true(event instanceof CookieChangeEvent);
   assert_equals(event.type, 'change');
   assert_equals(event.changed.length, 1);
   assert_equals(event.changed[0].name, 'cookie-name');
   assert_equals(event.changed[0].value, 'new-cookie-value');
   assert_equals(event.deleted.length, 0);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore fires change event for cookie overwritten by cookieStore.set()');
--- a/testing/web-platform/tests/cookie-store/cookieStore_getAll_arguments.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_getAll_arguments.tentative.https.window.js
@@ -1,130 +1,141 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   const cookies = await cookieStore.getAll();
   cookies.sort((a, b) => a.name.localeCompare(b.name));
   assert_equals(cookies.length, 2);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
   assert_equals(cookies[1].name, 'cookie-name-2');
   assert_equals(cookies[1].value, 'cookie-value-2');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with no arguments');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   const cookies = await cookieStore.getAll('cookie-name');
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with positional name');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   const cookies = await cookieStore.getAll({ name: 'cookie-name' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with name in options');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   const cookies = await cookieStore.getAll('cookie-name',
                                            { name: 'wrong-cookie-name' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with name in both positional arguments and options');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookies = await cookieStore.getAll({ name: 'cookie-name',
                                              matchType: 'equals' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
 
   const no_cookies = await cookieStore.getAll(
       'cookie-na', { matchType: 'equals' });
   assert_equals(no_cookies.length, 0);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.getAll with matchType explicitly set to equals');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   const cookies = await cookieStore.getAll({ name: 'cookie-name-',
                                              matchType: 'starts-with' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name-2');
   assert_equals(cookies[0].value, 'cookie-value-2');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with matchType set to starts-with');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
 
   await promise_rejects(testCase, new TypeError(), cookieStore.getAll(
       { name: 'cookie-name', matchType: 'invalid' }));
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
 }, 'cookieStore.getAll with invalid matchType');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookies = await cookieStore.getAll({ matchType: 'equals' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.getAll with matchType set to equals and missing name');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookies = await cookieStore.getAll({ matchType: 'starts-with' });
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.getAll with matchType set to starts-with and missing name');
--- a/testing/web-platform/tests/cookie-store/cookieStore_getAll_multiple.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_getAll_multiple.tentative.https.window.js
@@ -1,31 +1,26 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name-2', 'cookie-value-2');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-2');
+  });
   await cookieStore.set('cookie-name-3', 'cookie-value-3');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name-3');
+  });
 
   const cookies = await cookieStore.getAll();
   cookies.sort((a, b) => a.name.localeCompare(b.name));
   assert_equals(cookies.length, 3);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
   assert_equals(cookies[1].name, 'cookie-name-2');
   assert_equals(cookies[1].value, 'cookie-value-2');
   assert_equals(cookies[2].name, 'cookie-name-3');
   assert_equals(cookies[2].value, 'cookie-value-3');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-2'));
-  await async_cleanup(() => cookieStore.delete('cookie-name-3'));
 }, 'cookieStore.getAll returns multiple cookies written by cookieStore.set');
--- a/testing/web-platform/tests/cookie-store/cookieStore_getAll_set_basic.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_getAll_set_basic.tentative.https.window.js
@@ -1,22 +1,13 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookies = await cookieStore.getAll('cookie-name');
 
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.getAll returns the cookie written by cookieStore.set');
--- a/testing/web-platform/tests/cookie-store/cookieStore_get_arguments.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_get_arguments.tentative.https.window.js
@@ -1,105 +1,104 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get();
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with no arguments');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with positional name');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get({ name: 'cookie-name' });
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with name in options');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get('cookie-name',
                                        { name: 'wrong-cookie-name' });
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with name in both positional arguments and options');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get(
       'cookie-name', { matchType: 'equals' });
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
 
   const no_cookie = await cookieStore.get({ name: 'cookie-na',
                                             matchType: 'equals' });
   assert_equals(no_cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with matchType explicitly set to equals');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get({ name: 'cookie-na',
                                          matchType: 'starts-with' });
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with matchType set to starts-with');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   await promise_rejects(testCase, new TypeError(), cookieStore.get(
       { name: 'cookie-name', matchType: 'invalid' }));
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with invalid matchType');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get({ matchType: 'equals' });
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with matchType set to equals and missing name');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get({ matchType: 'starts-with' });
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get with matchType set to starts-with and missing name');
--- a/testing/web-platform/tests/cookie-store/cookieStore_get_delete_basic.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_get_delete_basic.tentative.https.window.js
@@ -1,20 +1,11 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+     await cookieStore.delete('cookie-name');
+  });
   await cookieStore.delete('cookie-name');
   const cookie = await cookieStore.get();
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get returns null for a cookie deleted by cookieStore.delete');
\ No newline at end of file
--- a/testing/web-platform/tests/cookie-store/cookieStore_get_set_across_frames.tentative.https.html
+++ b/testing/web-platform/tests/cookie-store/cookieStore_get_set_across_frames.tentative.https.html
@@ -14,29 +14,33 @@ promise_test(async t => {
   const iframe = document.getElementById('iframe');
   const frameCookieStore = iframe.contentWindow.cookieStore;
 
   const oldCookie = await frameCookieStore.get('cookie-name');
   assert_equals(oldCookie, null,
       'Precondition not met: cookie store should be empty');
 
   await cookieStore.set('cookie-name', 'cookie-value');
-  t.add_cleanup(() => cookieStore.delete('cookie-name'));
+  t.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const frameCookie = await frameCookieStore.get('cookie-name');
   assert_equals(frameCookie.value, 'cookie-value');
 }, 'cookieStore.get() sees cookieStore.set() in frame');
 
 promise_test(async t => {
   const iframe = document.getElementById('iframe');
   const frameCookieStore = iframe.contentWindow.cookieStore;
 
   const oldCookie = await frameCookieStore.get('cookie-name');
   assert_equals(oldCookie, null,
       'Precondition not met: cookie store should be empty');
 
   await frameCookieStore.set('cookie-name', 'cookie-value');
-  t.add_cleanup(() => frameCookieStore.delete('cookie-name'));
+  t.add_cleanup(async () => {
+    await frameCookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.value, 'cookie-value');
 }, 'cookieStore.get() in frame sees cookieStore.set()')
 </script>
--- a/testing/web-platform/tests/cookie-store/cookieStore_get_set_across_origins.tentative.sub.https.html
+++ b/testing/web-platform/tests/cookie-store/cookieStore_get_set_across_origins.tentative.sub.https.html
@@ -19,20 +19,19 @@ promise_test(async t => {
   const iframe = await createIframe(kCorsUrl, t);
   assert_true(iframe != null);
 
   iframe.contentWindow.postMessage({
     opname: 'set-cookie',
     name: 'cookie-name',
     value: 'cookie-value',
   }, kCorsBase);
-  t.add_cleanup(() => cookieStore.delete({
-    name: 'cookie-name',
-    domain: '{{host}}',
-  }));
+  t.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: '{{host}}' });
+  });
   await waitForMessage();
 
   const cookies = await cookieStore.getAll();
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-value');
 }, 'cookieStore.get() sees cookieStore.set() in cross-origin frame');
 
@@ -48,20 +47,19 @@ promise_test(async t => {
 
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.value, 'cookie-value');
 
   iframe.contentWindow.postMessage({
     opname: 'get-cookie',
     name: 'cookie-name',
   }, kCorsBase);
-  t.add_cleanup(() => cookieStore.delete({
-    name: 'cookie-name',
-    domain: '{{host}}',
-  }));
+  t.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: '{{host}}' });
+  });
 
   const message = await waitForMessage();
 
   const { frameCookie } = message;
   assert_not_equals(frameCookie, null);
   assert_equals(frameCookie.name, 'cookie-name');
   assert_equals(frameCookie.value, 'cookie-value');
 }, 'cookieStore.get() in cross-origin frame sees cookieStore.set()');
--- a/testing/web-platform/tests/cookie-store/cookieStore_get_set_basic.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_get_set_basic.tentative.https.window.js
@@ -1,21 +1,12 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
 
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.get returns the cookie written by cookieStore.set');
--- a/testing/web-platform/tests/cookie-store/cookieStore_set_arguments.tentative.https.window.js
+++ b/testing/web-platform/tests/cookie-store/cookieStore_set_arguments.tentative.https.window.js
@@ -1,161 +1,160 @@
 'use strict';
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with positional name and value');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set({ name: 'cookie-name', value: 'cookie-value' });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with name and value in options');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   cookieStore.set('cookie-name', 'cookie-value', { name: 'wrong-cookie-name' });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with name in both positional arguments and options');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   cookieStore.set('cookie-name', 'cookie-value',
                   { value: 'wrong-cookie-value' });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with value in both positional arguments and options');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsFromNow = Date.now() + tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { expires: tenYearsFromNow });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with expires set to a future timestamp');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsAgo = Date.now() - tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { expires: tenYearsAgo });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with expires set to a past timestamp');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsFromNow = Date.now() + tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { expires: new Date(tenYearsFromNow) });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with expires set to a future Date');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsAgo = Date.now() - tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { expires: new Date(tenYearsAgo) });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with expires set to a past Date');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsFromNow = Date.now() + tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       { name: 'cookie-name', value: 'cookie-value', expires: tenYearsFromNow });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with name and value in options and expires in the future');
 
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsAgo = Date.now() - tenYears;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set(
       { name: 'cookie-name', value: 'cookie-value', expires: tenYearsAgo });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with name and value in options and expires in the past');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { domain: currentDomain });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
-  });
 }, 'cookieStore.set with domain set to the current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   const subDomain = `sub.${currentDomain}`;
 
   await promise_rejects(testCase, new TypeError(), cookieStore.set(
@@ -179,96 +178,97 @@ promise_test(async testCase => {
    'current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentDomain = currentUrl.hostname;
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-value1');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name', 'cookie-value2',
                         { domain: currentDomain });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
+  });
 
   const cookies = await cookieStore.getAll('cookie-name');
   assert_equals(cookies.length, 2);
 
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[1].name, 'cookie-name');
 
   const values = cookies.map((cookie) => cookie.value);
   values.sort();
   assert_array_equals(values, ['cookie-value1', 'cookie-value2']);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete('cookie-name');
-    await cookieStore.delete({ name: 'cookie-name', domain: currentDomain });
-  });
 }, 'cookieStore.set default domain is null and differs from current hostname');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentPath = currentUrl.pathname;
   const currentDirectory =
       currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
   await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'cookie-value');
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
-  });
 }, 'cookieStore.set with path set to the current directory');
 
 promise_test(async testCase => {
   const currentUrl = new URL(self.location.href);
   const currentPath = currentUrl.pathname;
   const currentDirectory =
       currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
   const subDirectory = currentDirectory + "subdir/";
   await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
   await cookieStore.delete({ name: 'cookie-name', path: subDirectory });
 
   await cookieStore.set(
       'cookie-name', 'cookie-value', { path: subDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: subDirectory });
+  });
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete({ name: 'cookie-name', path: subDirectory });
-  });
 }, 'cookieStore.set with path set to a subdirectory of the current directory');
 
 promise_test(async testCase => {
   await cookieStore.delete('cookie-name');
 
   await cookieStore.set('cookie-name', 'cookie-old-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
   await cookieStore.set('cookie-name', 'cookie-new-value', { path: '/' });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name',  path: '/' });
+  });
 
   const cookies = await cookieStore.getAll('cookie-name');
   assert_equals(cookies.length, 1);
   assert_equals(cookies[0].name, 'cookie-name');
   assert_equals(cookies[0].value, 'cookie-new-value');
-
-  await async_cleanup(async () => {
-    await cookieStore.delete('cookie-name');
-    await cookieStore.delete({ name: 'cookie-name',  path: '/' });
-  });
 }, 'cookieStore.set default path is /');
 
 promise_test(async testCase => {
   await cookieStore.set('cookie-name', 'old-cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
 
   const cookie_attributes = await cookieStore.get('cookie-name');
   assert_equals(cookie_attributes.name, 'cookie-name');
   assert_equals(cookie_attributes.value, 'old-cookie-value');
 
   cookie_attributes.value = 'new-cookie-value';
   await cookieStore.set(cookie_attributes);
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'new-cookie-value');
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookieStore.set with get result');
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_cross_origin.tentative.https.sub.html
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_cross_origin.tentative.https.sub.html
@@ -22,19 +22,17 @@ promise_test(async t => {
       'serviceworker_cookieStore_cross_origin.js', '/does/not/exist');
 
 
   iframe.contentWindow.postMessage({
     opname: 'set-cookie',
     name: 'cookie-name',
     value: 'cookie-value',
   }, kCorsBase);
-  t.add_cleanup(() => {
-    cookieStore.delete('cookie-name');
-  });
+  t.add_cleanup(async () => { await cookieStore.delete('cookie-name'); });
 
   await waitForMessage();
 
   const { workerCookies } = await sendMessageOverChannel({ op: 'get-cookies' },
       serviceWorker);
 
   assert_equals(workerCookies.length, 1);
   assert_equals(workerCookies[0].name, 'cookie-name');
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions.js
@@ -19,26 +19,16 @@ 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) {}
   })());
 });
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 // Resolves when the service worker receives the 'activate' event.
 const kServiceWorkerActivatedPromise = new Promise(resolve => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
 // sort() comparator that uses the < operator.
 //
 // This is intended to be used for sorting strings. Using < is preferred to
@@ -91,29 +81,28 @@ function RearmCookieChangeReceivedPromis
   });
 }
 RearmCookieChangeReceivedPromise();
 
 promise_test(async testCase => {
   await kServiceWorkerActivatedPromise;
 
   await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
+  testCase.add_cleanup(() => { g_cookie_changes = []; });
 
   await g_cookie_change_received_promise;
+  testCase.add_cleanup(() => RearmCookieChangeReceivedPromise());
 
   assert_equals(g_cookie_changes.length, 1);
   const event = g_cookie_changes[0]
   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);
-
-  await async_cleanup(async () => {
-    await cookieStore.delete('cookie-name');
-    g_cookie_changes = [];
-    RearmCookieChangeReceivedPromise();
-  });
 }, 'cookiechange dispatched with cookie change that matches subscription');
 
 done();
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_basic.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_basic.js
@@ -12,26 +12,16 @@ 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) {}
   })());
 });
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 // Resolves when the service worker receives the 'activate' event.
 const kServiceWorkerActivatedPromise = new Promise(resolve => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
 promise_test(async testCase => {
   await kServiceWorkerActivatedPromise;
 
@@ -48,23 +38,24 @@ promise_test(async testCase => {
 
   const cookie_change_received_promise = new Promise((resolve) => {
     self.addEventListener('cookiechange', (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;
   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);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookiechange dispatched with cookie change that matches subscription ' +
    'to event handler registered with addEventListener');
 
 done();
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_empty.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_empty.js
@@ -11,26 +11,16 @@ 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) {}
   })());
 });
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/w3c/web-platform-tests/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 // Resolves when the service worker receives the 'activate' event.
 const kServiceWorkerActivatedPromise = new Promise(resolve => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
 promise_test(async testCase => {
   await kServiceWorkerActivatedPromise;
 
--- 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
@@ -12,46 +12,37 @@ 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) {}
   })());
 });
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/web-platform-tests/wpt/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 // Resolves when the service worker receives the 'activate' event.
 const kServiceWorkerActivatedPromise = new Promise(resolve => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
 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;
   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);
-
-  await async_cleanup(() => cookieStore.delete('cookie-name'));
 }, 'cookiechange dispatched with cookie change that matches subscription ' +
    'to event handler registered with oncookiechange');
 
 done();
--- a/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js
+++ b/testing/web-platform/tests/cookie-store/serviceworker_cookieStore_subscriptions_mismatch.js
@@ -12,48 +12,39 @@ 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) {}
   })());
 });
 
-// Workaround because add_cleanup doesn't support async functions yet.
-// See https://github.com/w3c/web-platform-tests/issues/6075
-async function async_cleanup(cleanup_function) {
-  try {
-    await cleanup_function();
-  } catch (e) {
-    // Errors in cleanup functions shouldn't result in test failures.
-  }
-}
-
 // Resolves when the service worker receives the 'activate' event.
 const kServiceWorkerActivatedPromise = new Promise(resolve => {
   self.addEventListener('activate', event => { resolve(); });
 });
 
 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;
   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');
-
-  await async_cleanup(async () => {
-    await cookieStore.delete('another-cookie-name');
-    await cookieStore.delete('cookie-name');
-  });
 }, 'cookiechange not dispatched for change that does not match subscription');
 
 done();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/align-content-computed.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().alignContent</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-content">
+<meta name="assert" content="align-content 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("align-content", "normal");
+
+test_computed_value("align-content", "baseline");
+test_computed_value("align-content", "last baseline");
+
+test_computed_value("align-content", "space-between");
+test_computed_value("align-content", "space-around");
+test_computed_value("align-content", "space-evenly");
+test_computed_value("align-content", "stretch");
+
+test_computed_value("align-content", "center");
+test_computed_value("align-content", "start");
+test_computed_value("align-content", "end");
+test_computed_value("align-content", "flex-start");
+test_computed_value("align-content", "flex-end");
+test_computed_value("align-content", "unsafe end");
+test_computed_value("align-content", "safe flex-start");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/align-items-computed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().alignItems</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-items">
+<meta name="assert" content="align-items 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("align-items", "normal");
+test_computed_value("align-items", "stretch");
+
+test_computed_value("align-items", "baseline");
+test_computed_value("align-items", "last baseline");
+
+test_computed_value("align-items", "center");
+test_computed_value("align-items", "start");
+test_computed_value("align-items", "end");
+test_computed_value("align-items", "self-start");
+test_computed_value("align-items", "self-end");
+test_computed_value("align-items", "flex-start");
+test_computed_value("align-items", "flex-end");
+test_computed_value("align-items", "unsafe center");
+test_computed_value("align-items", "safe self-end");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/align-self-computed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().alignSelf</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<meta name="assert" content="align-self 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("align-self", "auto");
+test_computed_value("align-self", "normal");
+test_computed_value("align-self", "stretch");
+
+test_computed_value("align-self", "baseline");
+test_computed_value("align-self", "last baseline");
+
+test_computed_value("align-self", "center");
+test_computed_value("align-self", "start");
+test_computed_value("align-self", "end");
+test_computed_value("align-self", "self-start");
+test_computed_value("align-self", "self-end");
+test_computed_value("align-self", "flex-start");
+test_computed_value("align-self", "flex-end");
+test_computed_value("align-self", "unsafe center");
+test_computed_value("align-self", "safe self-end");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-content-computed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().justifyContent</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content">
+<meta name="assert" content="justify-content 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("justify-content", "normal");
+
+test_computed_value("justify-content", "space-between");
+test_computed_value("justify-content", "space-around");
+test_computed_value("justify-content", "space-evenly");
+test_computed_value("justify-content", "stretch");
+
+test_computed_value("justify-content", "center");
+test_computed_value("justify-content", "start");
+test_computed_value("justify-content", "end");
+test_computed_value("justify-content", "flex-start");
+test_computed_value("justify-content", "flex-end");
+test_computed_value("justify-content", "unsafe end");
+test_computed_value("justify-content", "safe flex-start");
+test_computed_value("justify-content", "left");
+test_computed_value("justify-content", "unsafe right");
+</script>
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-align/parsing/justify-content-invalid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-content-invalid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-content with invalid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-content">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content">
 <meta name="assert" content="justify-content supports only the grammar 'normal | <content-distribution> | <overflow-position>? [ <content-position> | left | right ]'.">
 <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("justify-content", "auto");
--- a/testing/web-platform/tests/css/css-align/parsing/justify-content-valid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-content-valid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-content with valid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-content">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content">
 <meta name="assert" content="justify-content supports the full grammar 'normal | <content-distribution> | <overflow-position>? [ <content-position> | left | right ]'.">
 <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("justify-content", "normal");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-items-computed.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().justifyItems</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items">
+<meta name="assert" content="justify-items computed value is as specified.">
+<meta name="assert" content="justify-items legacy depends on inherited value.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+test_computed_value("justify-items", "normal");
+test_computed_value("justify-items", "stretch");
+
+test_computed_value("justify-items", "baseline");
+test_computed_value("justify-items", "last baseline");
+
+test_computed_value("justify-items", "center");
+test_computed_value("justify-items", "start");
+test_computed_value("justify-items", "end");
+test_computed_value("justify-items", "self-start");
+test_computed_value("justify-items", "self-end");
+test_computed_value("justify-items", "flex-start");
+test_computed_value("justify-items", "flex-end");
+test_computed_value("justify-items", "unsafe center");
+test_computed_value("justify-items", "safe self-end");
+test_computed_value("justify-items", "right");
+test_computed_value("justify-items", "safe left");
+
+test_computed_value("justify-items", "legacy", "normal");
+test_computed_value("justify-items", "legacy left");
+test_computed_value("justify-items", "legacy right");
+test_computed_value("justify-items", "legacy center");
+
+test(() => {
+  const container = document.getElementById('container');
+  const target = document.getElementById('target');
+  target.style.justifyItems = 'legacy';
+  container.style.justifyItems = 'legacy left';
+  assert_equals(getComputedStyle(target).justifyItems, 'legacy left');
+  container.style.justifyItems = 'legacy right';
+  assert_equals(getComputedStyle(target).justifyItems, 'legacy right');
+  container.style.justifyItems = 'legacy center';
+  assert_equals(getComputedStyle(target).justifyItems, 'legacy center');
+  container.style.justifyItems = 'flex-end';
+  assert_equals(getComputedStyle(target).justifyItems, 'normal');
+}, 'justify-items legacy depends on inherited value');
+</script>
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-align/parsing/justify-items-invalid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-items-invalid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-items with invalid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-items">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items">
 <meta name="assert" content="justify-items supports only the grammar 'normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ] | legacy | legacy && [ left | right | center ]'.">
 <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("justify-items", "auto");
--- a/testing/web-platform/tests/css/css-align/parsing/justify-items-valid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-items-valid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-items with valid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-items">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items">
 <meta name="assert" content="justify-items supports the full grammar 'normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ] | legacy | legacy && [ left | right | center ]'.">
 <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("justify-items", "normal");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-self-computed.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().justifySelf</title>
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<meta name="assert" content="justify-self 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("justify-self", "auto");
+test_computed_value("justify-self", "normal");
+test_computed_value("justify-self", "stretch");
+
+test_computed_value("justify-self", "baseline");
+test_computed_value("justify-self", "last baseline");
+
+test_computed_value("justify-self", "center");
+test_computed_value("justify-self", "start");
+test_computed_value("justify-self", "end");
+test_computed_value("justify-self", "self-start");
+test_computed_value("justify-self", "self-end");
+test_computed_value("justify-self", "flex-start");
+test_computed_value("justify-self", "flex-end");
+test_computed_value("justify-self", "unsafe center");
+test_computed_value("justify-self", "safe self-end");
+test_computed_value("justify-self", "left");
+test_computed_value("justify-self", "unsafe right");
+</script>
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-align/parsing/justify-self-invalid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-self-invalid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-self with invalid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
 <meta name="assert" content="justify-self supports only the grammar 'auto | normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ]'.">
 <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("justify-self", "baseline last");
--- a/testing/web-platform/tests/css/css-align/parsing/justify-self-valid.html
+++ b/testing/web-platform/tests/css/css-align/parsing/justify-self-valid.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>CSS Box Alignment Level 3: parsing justify-self with valid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
 <meta name="assert" content="justify-self supports the full grammar 'auto | normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ]'.">
 <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("justify-self", "auto");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-box/parsing/margin-computed.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS basic box model: getComputedStyle().margin</title>
+<link rel="help" href="https://drafts.csswg.org/css-box-3/#propdef-margin">
+<meta name="assert" content="margin computed value has absolute lengths.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #parent {
+    width: 200px;
+  }
+  #target {
+    width: 0px;
+    font-size: 40px;
+  }
+</style>
+</head>
+<body>
+<div id="parent">
+  <div id="target"></div>
+</div>
+<script>
+test_computed_value("margin", "10px");
+test_computed_value("margin", "10px 20px 30px 40px");
+test_computed_value("margin", "calc(0.5em + 10px)", "30px");
+test_computed_value("margin", "30%", "60px");
+
+test_computed_value("margin-top", "10px");
+test_computed_value("margin-right", "20px");
+test_computed_value("margin-bottom", "30px");
+test_computed_value("margin-left", "40px");
+</script>
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001-ref.html
+++ b/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001-ref.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS container Layout Test Reference</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
   .container {
     font: 10px/1 Ahem;
     margin: 10px;
     background: grey;
     writing-mode: vertical-lr;
   }
 
--- a/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001.html
+++ b/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001.html
@@ -1,16 +1,17 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Grid Layout Test: Grid container with scrollbars</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-grid/#grid-model">
 <link rel="match" href="grid-container-scrollbar-vertical-lr-001-ref.html">
 <meta name="assert" content="This test verifies that scrollbars are properly painted on grid containers, and are shown in the expected position depending on the direction.">
 <link href="support/grid.css" rel="stylesheet">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
   .grid {
     font: 10px/1 Ahem;
     margin: 10px;
     writing-mode: vertical-lr;
   }
 
   .scrollX {
--- a/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001-ref.html
+++ b/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001-ref.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS container Layout Test Reference</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
   .container {
     font: 10px/1 Ahem;
     margin: 10px;
     background: grey;
     writing-mode: vertical-rl;
   }
 
--- a/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001.html
+++ b/testing/web-platform/tests/css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001.html
@@ -1,16 +1,17 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Grid Layout Test: Grid container with scrollbars</title>
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="https://drafts.csswg.org/css-grid/#grid-model">
 <link rel="match" href="grid-container-scrollbar-vertical-rl-001-ref.html">
 <meta name="assert" content="This test verifies that scrollbars are properly painted on grid containers, and are shown in the expected position depending on the direction.">
 <link href="support/grid.css" rel="stylesheet">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
   .grid {
     font: 10px/1 Ahem;
     margin: 10px;
     writing-mode: vertical-rl;
   }
 
   .scrollX {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-logical/parsing/block-size-computed.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedStyle().blockSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical-1/#dimension-properties">
+<meta name="assert" content="block-size computed value is an absolute length.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #parent {
+    height: 300px;
+  }
+  #target {
+    width: 0px;
+    height: 0px;
+    font-size: 40px;
+  }
+  #child {
+    height: 80px;
+  }
+</style>
+</head>
+<body>
+<div id="parent">
+  <div id="target">
+    <div id="child">
+    </div>
+  </div>
+</div>
+<script>
+test_computed_value("block-size", "auto", "80px"); // child height
+
+test_computed_value("block-size", "10px");
+test_computed_value("block-size", "20%", "60px");
+test_computed_value("block-size", "calc(0.5em + 10px)", "30px");
+test_computed_value("block-size", "calc(-0.5em + 10px)", "0px");
+
+test_computed_value("block-size", "min-content", "80px"); // child height
+test_computed_value("block-size", "max-content", "80px");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-logical/parsing/inline-size-computed.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Logical Properties and Values: getComputedStyle().inlineSize</title>
+<link rel="help" href="https://drafts.csswg.org/css-logical-1/#dimension-properties">
+<meta name="assert" content="inline-size computed value is an absolute length.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #parent {
+    width: 200px;
+  }
+  #target {
+    width: 0px;
+    height: 0px;
+    font-size: 40px;
+  }
+  #child {
+    width: 60px;
+  }
+</style>
+</head>
+<body>
+<div id="parent">
+  <div id="target">
+    <div id="child">
+    </div>
+  </div>
+</div>
+<script>
+test_computed_value("inline-size", "auto", "200px"); // parent width
+
+test_computed_value("inline-size", "10px");
+test_computed_value("inline-size", "20%", "40px");
+test_computed_value("inline-size", "calc(0.5em + 10px)", "30px");
+test_computed_value("inline-size", "calc(-0.5em + 10px)", "0px");
+
+test_computed_value("inline-size", "min-content", "60px"); // child width
+test_computed_value("inline-size", "max-content", "60px");
+</script>
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-paint-api/background-image-alpha.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/background-image-alpha.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="background-image-alpha-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-opaque {
--- a/testing/web-platform/tests/css/css-paint-api/background-image-multiple.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/background-image-multiple.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="background-image-multiple-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(n0), paint(n1), paint(n2);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/background-image-tiled.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/background-image-tiled.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="background-image-tiled-ref.html">
 <style>
     div {
         display: inline-block;
         width: 100px;
         height: 100px;
     }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-background-image-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-background-image-001.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-background-image-001-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 100px;
   height: 100px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-background-image-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-background-image-002.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-background-image-002-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 200px;
   height: 200px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-001.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-background-image-tiled-001-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 100px;
   height: 100px;
   background: paint(geometry) top left/50% 50% repeat-x;
 }
--- a/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-002.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-background-image-tiled-002-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 100px;
   height: 100px;
   background: paint(geometry) center right/50% 20% no-repeat;
 }
--- a/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-003.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-background-image-tiled-003.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-background-image-tiled-003-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 100px;
   height: 100px;
   background: paint(geometry) center center/60px 80px no-repeat;
 }
--- a/testing/web-platform/tests/css/css-paint-api/geometry-border-image-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-border-image-001.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-border-image-001-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 200px;
   height: 200px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-border-image-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-border-image-002.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-border-image-002-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 50px;
   height: 50px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-border-image-003.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-border-image-003.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-border-image-003-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 60px;
   height: 100px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-border-image-004.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-border-image-004.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-border-image-004-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 100px;
   height: 100px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/geometry-with-float-size.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/geometry-with-float-size.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="geometry-with-float-size-ref.html">
-<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/#dom-css-paintworklet">
 <style>
 .container {
   width: 100.5px;
   height: 200.5px;
 }
 #canvas-geometry {
   background-image: paint(geometry);
 }
--- a/testing/web-platform/tests/css/css-paint-api/hidpi/canvas-transform.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/hidpi/canvas-transform.https.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/#dom-css-paintworklet">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="canvas-transform-ref.html">
 <style>
 .container {
   width: 270px;
   height: 275px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/hidpi/device-pixel-ratio.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/hidpi/device-pixel-ratio.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="device-pixel-ratio-ref.html">
 <style>
 html, body { margin: 0; padding: 0; }
 .container {
   width: 150px;
   height: 150px;
 }
 
--- a/testing/web-platform/tests/css/css-paint-api/idlharness.html
+++ b/testing/web-platform/tests/css/css-paint-api/idlharness.html
@@ -1,11 +1,11 @@
 <!doctype html>
 <title>CSS Painting API IDL tests</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-paint-api-1/">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/WebIDLParser.js"></script>
 <script src="/resources/idlharness.js"></script>
 <script>
   "use strict";
 
   idl_test(
--- a/testing/web-platform/tests/css/css-paint-api/invalid-image-constructor-error.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/invalid-image-constructor-error.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="invalid-image-constructor-error-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(errorIndicator), paint(successIndicator);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/invalid-image-paint-error.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/invalid-image-paint-error.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="invalid-image-paint-error-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(errorIndicator), paint(successIndicator);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/invalid-image-pending-script.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/invalid-image-pending-script.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="invalid-image-pending-script-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(invalid), paint(successIndicator);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/overdraw.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/overdraw.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="overdraw-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(green);
         background-color: red;
     }
--- a/testing/web-platform/tests/css/css-paint-api/paint-arguments.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint-arguments.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-box-green {
--- a/testing/web-platform/tests/css/css-paint-api/paint-function-arguments.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint-function-arguments.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint-function-arguments-ref.html">
 <style>
 .container {
   width: 200px;
   height: 200px;
 }
 
 #canvas-box-1 {
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-composite.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-composite.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-composite-ref.html">
 <style>
     div {
         display: inline-block;
         width: 80px;
         height: 80px;
     }
     #source-over { background-image: paint(source-over); }
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-filter.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-filter.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-filter-ref.html">
 <style>
     div {
         display: inline-block;
         width: 100px;
         height: 100px;
     }
     #filter-none { background-image: paint(filter-none); }
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-gradient.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-gradient.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-gradient-ref.html">
 <style>
     #output {
         width: 200px;
         height: 100px;
         background-image: paint(gradients);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-image.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-image.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-image-ref.html">
 <style>
 #output {
     width: 300px;
     height: 300px;
     background-image: paint(image);
     border-image: url("./resources/html5.png");
 }
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-paths.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-paths.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-paths-ref.html">
 <style>
     #output {
         width: 300px;
         height: 400px;
         background-image: paint(paths);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-rects.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-rects.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-rects-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-image: paint(rects);
         background-color: blue;
     }
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-shadows.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-shadows.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-shadows-ref.html">
 <style>
     #output {
         width: 200px;
         height: 100px;
         background-image: paint(shadows);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/paint2d-transform.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/paint2d-transform.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="paint2d-transform-ref.html">
 <style>
     #output {
         width: 200px;
         height: 200px;
         background-image: paint(transform);
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-001.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-002.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-003.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-003.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-004.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-004.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-005.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-005.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-006.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-006.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-007.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-007.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-008.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-008.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-009.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-009.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-010.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-010.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-011.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-011.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-012.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-012.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-013.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-013.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-014.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-014.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-015.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-015.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-016.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-016.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-017.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-017.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-018-ref.html">
 <style>
 .container {
     width: 100px;
     height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-019.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-019.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-020.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-020.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-021.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-021.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-022.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-022.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
 }
 
 #canvas-geometry {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-001.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;angle&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-002.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;color&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-003.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-003.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;integer&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-004.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-004.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;length-percentage&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-005.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-005.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;length&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-006.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-006.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;number&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-007.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-007.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;percentage&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-008.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-008.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;resolution&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-009.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-009.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated &lt;time&gt; values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-010.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-interpolation-010.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Interpolated list values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-invalidation-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-invalidation-001.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Registering a property causes invalidation for initial value</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <body>
 <style>
 #target {
   background: paint(geometry);
   width: 100px;
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-invalidation-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-invalidation-002.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Registering a property causes invalidation for applied value</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <body>
 <style>
 #target {
   background: paint(geometry);
   width: 100px;
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-stylemap.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-stylemap.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Test styleMap functions</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <style>
   #target {
     width: 100px;
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-001.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-001.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Initial values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-002.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-002.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Inherited values reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="outer">
   <div id="target"></div>
 </div>
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-003.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-003.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of *-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-004.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-004.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;angle&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-005.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-005.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;color&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-006.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-006.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;custom-ident&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-007.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-007.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;image&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-008.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-008.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;integer&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-009.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-009.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;length-percentage&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-010.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-010.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;length&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-011.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-011.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;number&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-012.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-012.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;percentage&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-013.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-013.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;resolution&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-014.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-014.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;time&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-015.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-015.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of &lt;url&gt;-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-016.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-016.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of ident-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-017.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-017.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of ident-properties reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/registered-property-value-018.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/registered-property-value-018.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <title>Values of lists reach worklet</title>
-<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="parse-input-arguments-ref.html">
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/worklet-reftest.js"></script>
 <script src="./resources/utils.js"></script>
 <body>
 <div id="target"></div>
 <script>
     try {
--- a/testing/web-platform/tests/css/css-paint-api/style-background-image.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/style-background-image.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="style-background-image-ref.html">
 <style>
 .container {
   width: 100px;
   height: 100px;
   margin-left: 2px;
   --foo: bar;
 }
--- a/testing/web-platform/tests/css/css-paint-api/style-before-pseudo.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/style-before-pseudo.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="style-before-pseudo-ref.html">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 div {
     margin-left: 3px;
 }
 
 div::before {
--- a/testing/web-platform/tests/css/css-paint-api/style-first-letter-pseudo.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/style-first-letter-pseudo.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="style-first-letter-pseudo-ref.html">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 div {
     color: rgb(0, 255, 0);
     line-height: 1px;
     height: 10px;
 }
--- a/testing/web-platform/tests/css/css-paint-api/valid-image-after-load.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/valid-image-after-load.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="valid-image-after-load-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-color: red;
     }
 </style>
--- a/testing/web-platform/tests/css/css-paint-api/valid-image-before-load.https.html
+++ b/testing/web-platform/tests/css/css-paint-api/valid-image-before-load.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
+<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/">
 <link rel="match" href="valid-image-before-load-ref.html">
 <style>
     #output {
         width: 100px;
         height: 100px;
         background-color: red;
     }
 </style>
--- a/testing/web-platform/tests/css/css-properties-values-api/conditional-rules.html
+++ b/testing/web-platform/tests/css/css-properties-values-api/conditional-rules.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#conditional-rules" />
+<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#conditional-rules">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <script>
   CSS.registerProperty({
     name: '--length',
     syntax: '<length>',
     initialValue: '0px',
@@ -30,13 +30,14 @@ test(function() {
 
 test(function() {
   assert_true(CSS.supports('--length: red'));
   assert_true(CSS.supports('--length: 10px'));
   assert_true(CSS.supports('--length: anything, really'));
 }, 'CSS.supports(conditionText) should ignore registered syntax');
 
 test(function() {
-  assert_false(CSS.supports('--length', 'red'));
+  assert_true(CSS.supports('--length', 'red'));
   assert_true(CSS.supports('--length', '10px'));
-}, 'CSS.supports(property, value) should parse against registered syntax');
+  assert_true(CSS.supports('--length', ' anything, really'));
+}, 'CSS.supports(property, value) should ignore registered syntax');
 
 </script>
--- a/testing/web-platform/tests/css/css-properties-values-api/register-property-syntax-parsing.html
+++ b/testing/web-platform/tests/css/css-properties-values-api/register-property-syntax-parsing.html
@@ -49,16 +49,18 @@ assert_valid("<length>", "calc(2px*4 + 1
 assert_valid("<length>", "7.1e-4cm");
 assert_valid("<length>", "calc(7in - 12px)");
 assert_valid("<length>+", "2px 7px calc(8px)");
 assert_valid("<length>#", "2px, 7px, calc(8px)");
 assert_valid("<percentage>", "-9.3e3%");
 assert_valid("<length-percentage>", "-54%");
 assert_valid("<length-percentage>", "0");
 assert_valid("<length-percentage>", "calc(-11px + 10.4%)");
+assert_valid("<length>", "10vmin");
+assert_valid("<percentage> | <length>+", "calc(100vh - 10px) 30px");
 
 assert_valid("<number>", "-109");
 assert_valid("<number>", "2.3e4");
 assert_valid("<integer>", "-109");
 assert_valid("<integer>", "19");
 assert_valid("<integer>", "calc(1)");
 assert_valid("<integer>", "calc(1 + 2)");
 assert_valid("<integer>", "calc(3.1415)");
@@ -178,24 +180,22 @@ assert_invalid("*", "var(--foo)");
 
 assert_invalid("banana", "bAnAnA");
 assert_invalid("<length>", "var(--moo)");
 assert_invalid("<length>", "10");
 assert_invalid("<length>", "10%");
 assert_invalid("<length>", "calc(5px + 10%)");
 assert_invalid("<length>", "calc(5px * 3px / 6px)");
 assert_invalid("<length>", "10em");
-assert_invalid("<length>", "10vmin");
 assert_invalid("<length>", "calc(4px + 3em)");
 assert_invalid("<length>", "calc(4px + calc(8 * 2em))");
 assert_invalid("<length>+", "calc(2ex + 16px)");
 assert_invalid("<length>+", "10px calc(20px + 4rem)");
 assert_invalid("<length>+", "");
 assert_invalid("<length>#", "");
-assert_invalid("<percentage> | <length>+", "calc(100vh - 10px) 30px");
 assert_invalid("<length>", "10px;");
 assert_invalid("<length-percentage>", "calc(2px + 10% + 7ex)");
 assert_invalid("<percentage>", "0");
 assert_invalid("<integer>", "1.0");
 assert_invalid("<integer>", "1e0");
 assert_invalid("<number>|foo", "foo var(--foo, bla)");
 assert_invalid("Foo | bar", "foo");
 assert_invalid("Foo | bar", "Bar");
--- a/testing/web-platform/tests/css/css-properties-values-api/unit-cycles.html
+++ b/testing/web-platform/tests/css/css-properties-values-api/unit-cycles.html
@@ -91,99 +91,96 @@
         target.style = 'font-size: var(--font-size-px);';
         assert_property_equals('font-size', '42px');
         assert_property_equals('--font-size-px', '42px');
     }, 'Non-font-dependent variables can be used in font-size');
 
     test(function() {
         target.style = 'font-size: var(--font-size-em);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-em', compute_dimension('2em', 'unset'));
+        assert_property_equals('--font-size-em', '');
     }, 'Lengths with em units may not be referenced from font-size');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ex);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-ex', compute_dimension('2ex', 'unset'));
+        assert_property_equals('--font-size-ex', '');
     }, 'Lengths with ex units may not be referenced from font-size');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ch);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-ch', compute_dimension('2ch', 'unset'));
+        assert_property_equals('--font-size-ch', '');
     }, 'Lengths with ch units may not be referenced from font-size');
 
     test(function() {
         target.style = 'font-size: var(--font-size-rem);';
         let expected = compute_dimension('2rem', 'unset', document.documentElement);
         assert_property_equals('--font-size-rem', expected);
         assert_property_equals('font-size', expected);
     }, 'Lengths with rem units may be referenced from font-size on non-root element');
 
     test(function() {
         let root = document.documentElement;
-        let expected1rem = compute_dimension('1rem', 'unset', root);
-        let expected2rem = compute_dimension('2rem', 'unset', root);
         root.style = 'font-size: var(--font-size-rem);';
-        assert_property_equals('font-size', expected1rem, root);
-        assert_property_equals('--font-size-rem', expected2rem, root);
+        assert_property_equals('font-size', unsetFontSize, root);
+        assert_property_equals('--font-size-rem', '', root);
     }, 'Lengths with rem units may not be referenced from font-size on root element');
 
     test(function() {
         target.style = 'font-size: var(--noexist, var(--font-size-em));';
         assert_property_equals('font-size', unsetFontSize);
     }, 'Fallback may not use font-relative units');
 
     test(function() {
         target.style = 'font-size: var(--font-size-em, 42px);';
-        assert_property_equals('font-size', '42px');
-    }, 'Fallback triggered when em unit cycle is detected');
+        assert_property_equals('font-size', unsetFontSize);
+    }, 'Fallback not triggered while inside em unit cycle');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ex, 42px);';
-        assert_property_equals('font-size', '42px');
-    }, 'Fallback triggered when ex unit cycle is detected');
+        assert_property_equals('font-size', unsetFontSize);
+    }, 'Fallback not triggered while inside ex unit cycle');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ch, 42px);';
-        assert_property_equals('font-size', '42px');
-    }, 'Fallback triggered when ch unit cycle is detected');
+        assert_property_equals('font-size', unsetFontSize);
+    }, 'Fallback not triggered while inside ch unit cycle');
 
     test(function() {
         let root = document.documentElement;
         root.style = 'font-size: var(--font-size-rem, 42px);';
-        assert_property_equals('font-size', '42px', root);
+        assert_property_equals('font-size', unsetFontSize, root);
         root.style = 'font-size: unset;';
-    }, 'Fallback triggered when rem unit cycle is detected on root element');
+    }, 'Fallback not triggered while inside rem unit cycle on root element');
 
     test(function() {
         target.style = 'font-size: var(--font-size-em-via-var);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-em-via-var', compute_dimension('10em', 'unset'));
+        assert_property_equals('--font-size-em-via-var', '');
     }, 'Lengths with em units are detected via var references');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ex-via-var);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-ex-via-var', compute_dimension('10ex', 'unset'));
+        assert_property_equals('--font-size-ex-via-var', '');
     }, 'Lengths with ex units are detected via var references');
 
     test(function() {
         target.style = 'font-size: var(--font-size-ch-via-var);';
         assert_property_equals('font-size', unsetFontSize);
-        assert_property_equals('--font-size-ch-via-var', compute_dimension('10ch', 'unset'));
+        assert_property_equals('--font-size-ch-via-var', '');
     }, 'Lengths with ch units are detected via var references');
 
     test(function() {
         let root = document.documentElement;
-        let expected1rem = compute_dimension('1rem', 'unset', root);
-        let expected10rem = compute_dimension('10rem', 'unset', root);
         root.style = 'font-size: var(--font-size-rem-via-var);';
-        assert_property_equals('font-size', expected1rem, root);
-        assert_property_equals('--font-size-rem-via-var', expected10rem, root);
+        assert_property_equals('font-size', unsetFontSize, root);
+        assert_property_equals('--font-size-rem-via-var', '', root);
+        root.style = 'font-size: unset';
     }, 'Lengths with rem units are detected via var references');
 
     test(function() {
         let expected4em = compute_dimension('4em', 'unset');
         target.style = 'font-size: var(--font-size-em-inherited);';
         assert_property_equals('font-size', expected4em);
         assert_property_equals('--font-size-em-inherited', expected4em);
     }, 'Inherited lengths with em units may be used');
--- a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-on-root-element.html
+++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-on-root-element.html
@@ -1,42 +1,73 @@
 <!DOCTYPE html>
-<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type"/>
-<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#principal-flow"/>
+<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" />
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#principal-flow" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
 html {
   height: 3000px;
-  scroll-snap-type: inline mandatory;
+  width: 3000px;
 }
+
 #target {
   position: absolute;
   background-color: blue;
   top: 1000px;
-  width: 100%;
+  left: 100px;
+
+  width: 100vw;
   height: 100px;
 }
 </style>
 <div id="target"></div>
 <script>
 const documentHeight = document.documentElement.clientHeight;
-test(() => {
-  target.style.scrollSnapAlign = "end start";
 
-  window.scrollTo(0, 1000);
+function cleanup() {
+  document.documentElement.style.scrollSnapType = "none";
+  target.style.scrollSnapAlign = "";
+  document.body.style.writingMode = "";
+  window.scrollTo(0, 0);
+}
+
+test(t => {
+  t.add_cleanup(cleanup);
+  document.documentElement.style.scrollSnapType = "y mandatory";
+  target.style.scrollSnapAlign = "end none";
+
+  window.scrollTo(0, 800);
 
   // `target y (1000px)` + `target height (100px)` - document height.
   assert_equals(document.scrollingElement.scrollTop, 1100 - documentHeight);
-
-  target.style.scrollSnapAlign = "";
-  window.scrollTo(0, 0);
+  assert_equals(document.scrollingElement.scrollLeft, 0, "x should not snap");
 }, "The scroll-snap-type on the root element is applied");
 
-test(() => {
-  document.body.style.writingMode = "vertical-rl";
-  target.style.scrollSnapAlign = "start end";
+test(t => {
+  t.add_cleanup(cleanup);
+
+  document.documentElement.style.scrollSnapType = "inline mandatory";
+  document.body.style.writingMode = "vertical-lr";
+  target.style.scrollSnapAlign = "none end";
+
+  window.scrollTo(200, 800);
 
-  window.scrollTo(0, 1000);
-  // `target y (1000px)` + `target height (100px)` - document height.
-  assert_equals(document.scrollingElement.scrollTop, 1100 - documentHeight);
-}, "The writing-mode on the body is used");
-</script>
+  // Since inline axis is vertical, scrolling viewport vertically on block
+  // axis should snap.
+  assert_equals(document.scrollingElement.scrollTop, 1100 - documentHeight, "inline should snap");
+  // `target x (100px)`.
+  assert_equals(document.scrollingElement.scrollLeft, 200, "block should not snap");
+}, "The writing-mode (vertical-lr) on the body is used");
+
+test(t => {
+  t.add_cleanup(cleanup);
+
+  document.documentElement.style.scrollSnapType = "inline mandatory";
+  document.body.style.writingMode = "horizontal-tb"; // inline is horizontal
+  target.style.scrollSnapAlign = "none start";
+
+  window.scrollTo(200, 800);
+
+  assert_equals(document.scrollingElement.scrollLeft, 100, "inline should snap");
+  assert_equals(document.scrollingElement.scrollTop, 800, "block should not snap");
+}, "The writing-mode (horizontal-tb) on the body is used ");
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-tables/percent-width-cell-dynamic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/984642" />
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<style>
+html { overflow: hidden; }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="target">
+  <div style="width: 10%;">
+    <div style="display: inline-table;">
+      <div style="display: table-cell; width: 100%;">
+        <span style="display: inline-block; width: 100%; height: 100px; background: green;"></span>
+      </div>
+      <div style="display: table-cell;">
+        <span style="display: inline-block; width: 10px; height: 100px; background: green;"></span>
+      </div>
+     </div>
+  </div>
+</div>
+<script>
+document.body.offsetTop;
+document.getElementById('target').style.width = '1000px';
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/ja/css-text-line-break-ja-pr-loose.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (loose,ja)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is Japanese, and line-break:loose, a browser allows a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="ja">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="ja">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if ja and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/ja/css-text-line-break-ja-pr-normal.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (normal,ja)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is Japanese, and line-break:normal, a browser will NOT allow a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="ja">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="ja">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if ja and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/ja/css-text-line-break-ja-pr-strict.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (strict,ja)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is Japanese, and line-break:strict, a browser will NOT allow a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="ja">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="ja">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if ja and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cj-loose.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>Line-break:loose, Conditional Japanese Starter (CJ) (de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="The browser allows a conditional Japanese starter at the beginning of a line; the langauge not being Chinese or Japanese makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cj-normal.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>Line-break:normal, Conditional Japanese Starter (CJ) (de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="The browser allows a conditional Japanese starter at the beginning of a line; the langauge not being Chinese or Japanese makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cj-strict.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>line-break:strict, Conditional Japanese Starter (CJ) (de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="With line-break:strict, a browser will NOT allow a conditional Japanese starter at the beginning of a line; the langauge not being Chinese or Japanese makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cpm-loose.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cpm-normal.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-cpm-strict.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-loose.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-normal.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-hyphens-strict.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-in-loose.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser allows a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-in-normal.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-in-strict.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-iteration-loose.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser allows a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-iteration-normal.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-iteration-strict.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-po-loose.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PO AFW (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser wll NOT allow a break before a PO character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `00B0  DEGREE SIGN
+2030  PER MILLE SIGN
+2032  PRIME
+2033  DOUBLE PRIME
+2035  REVERSED PRIME
+2103  DEGREE CELSIUS
+2109  DEGREE FAHRENHEIT
+FE6A  SMALL PERCENT SIGN
+FF05  FULLWIDTH PERCENT SIGN
+FFE0  FULLWIDTH CENT SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-po-normal.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PO AFW (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before a PO character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `00B0  DEGREE SIGN
+2030  PER MILLE SIGN
+2032  PRIME
+2033  DOUBLE PRIME
+2035  REVERSED PRIME
+2103  DEGREE CELSIUS
+2109  DEGREE FAHRENHEIT
+FE6A  SMALL PERCENT SIGN
+FF05  FULLWIDTH PERCENT SIGN
+FFE0  FULLWIDTH CENT SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-po-strict.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PO AFW (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before a PO character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `00B0  DEGREE SIGN
+2030  PER MILLE SIGN
+2032  PRIME
+2033  DOUBLE PRIME
+2035  REVERSED PRIME
+2103  DEGREE CELSIUS
+2109  DEGREE FAHRENHEIT
+FE6A  SMALL PERCENT SIGN
+FF05  FULLWIDTH PERCENT SIGN
+FFE0  FULLWIDTH CENT SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-pr-loose.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (loose,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:loose, a browser wll NOT allow a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-pr-normal.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (normal,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:normal, a browser will NOT allow a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/other-lang/css-text-line-break-de-pr-strict.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PR AFW (strict,de)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is neither Japanese nor Chinese, and line-break:strict, a browser will NOT allow a break before a PR character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `00B1  PLUS-MINUS SIGN
+20AC  EURO SIGN
+2116  NUMERO SIGN
+FE69  SMALL DOLLAR SIGN
+FF04  FULLWIDTH DOLLAR SIGN
+FFE1  FULLWIDTH POUND SIGN
+FFE5  FULLWIDTH YEN SIGN
+FFE6  FULLWIDTH WON SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="de">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="de">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if de and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cj-loose.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>Line-break:loose, Conditional Japanese Starter (CJ) (unknown lang)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="The browser allows a conditional Japanese starter at the beginning of a line; the langauge being unkonwn makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cj-normal.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>Line-break:normal, Conditional Japanese Starter (CJ) (unknown lang)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="The browser allows a conditional Japanese starter at the beginning of a line; the langauge being unknown makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cj-strict.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>line-break:strict, Conditional Japanese Starter (CJ) (language unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="With line-break:strict, a browser will NOT allow a conditional Japanese starter at the beginning of a line; the langauge being unknown makes no difference.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `3041  HIRAGANA LETTER SMALL A
+3043  HIRAGANA LETTER SMALL I
+3045  HIRAGANA LETTER SMALL U
+3047  HIRAGANA LETTER SMALL E
+3049  HIRAGANA LETTER SMALL O
+3063  HIRAGANA LETTER SMALL TU
+3083  HIRAGANA LETTER SMALL YA
+3085  HIRAGANA LETTER SMALL YU
+3087  HIRAGANA LETTER SMALL YO
+308E  HIRAGANA LETTER SMALL WA
+3095  HIRAGANA LETTER SMALL KA
+3096  HIRAGANA LETTER SMALL KE
+30A1  KATAKANA LETTER SMALL A
+30A3  KATAKANA LETTER SMALL I
+30A5  KATAKANA LETTER SMALL U
+30A7  KATAKANA LETTER SMALL E
+30A9  KATAKANA LETTER SMALL O
+30C3  KATAKANA LETTER SMALL TU
+30E3  KATAKANA LETTER SMALL YA
+30E5  KATAKANA LETTER SMALL YU
+30E7  KATAKANA LETTER SMALL YO
+30EE  KATAKANA LETTER SMALL WA
+30F5  KATAKANA LETTER SMALL KA
+30F6  KATAKANA LETTER SMALL KE
+30FC  KATAKANA-HIRAGANA PROLONGED SOUND MARK
+31F0  KATAKANA LETTER SMALL KU
+31F1  KATAKANA LETTER SMALL SI
+31F2  KATAKANA LETTER SMALL SU
+31F3  KATAKANA LETTER SMALL TO
+31F4  KATAKANA LETTER SMALL NU
+31F5  KATAKANA LETTER SMALL HA
+31F6  KATAKANA LETTER SMALL HI
+31F7  KATAKANA LETTER SMALL HU
+31F8  KATAKANA LETTER SMALL HE
+31F9  KATAKANA LETTER SMALL HO
+31FA  KATAKANA LETTER SMALL MU
+31FB  KATAKANA LETTER SMALL RA
+31FC  KATAKANA LETTER SMALL RI
+31FD  KATAKANA LETTER SMALL RU
+31FE  KATAKANA LETTER SMALL RE
+31FF  KATAKANA LETTER SMALL RO
+FF67  HALFWIDTH KATAKANA LETTER SMALL A
+FF68  HALFWIDTH KATAKANA LETTER SMALL I
+FF69  HALFWIDTH KATAKANA LETTER SMALL U
+FF6A  HALFWIDTH KATAKANA LETTER SMALL E
+FF6B  HALFWIDTH KATAKANA LETTER SMALL O
+FF6C  HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D  HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E  HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F  HALFWIDTH KATAKANA LETTER SMALL TU
+FF70  HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cpm-loose.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (loose,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:loose, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknonw loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cpm-normal.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (normal,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:normal, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-cpm-strict.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: centred punctuation (strict,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unkonwn, and line-break:strict, a browser will NOT allow a break before one of the centred punctuation characters listed.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `30FB  KATAKANA MIDDLE DOT
+FF1A  FULLWIDTH COLON
+FF1B  FULLWIDTH SEMICOLON
+FF65  HALFWIDTH KATAKANA MIDDLE DOT
+203C  DOUBLE EXCLAMATION MARK
+2047  DOUBLE QUESTION MARK
+2048  QUESTION EXCLAMATION MARK
+2049  EXCLAMATION QUESTION MARK
+FF01  FULLWIDTH EXCLAMATION MARK
+FF1F  FULLWIDTH QUESTION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-loose.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (loose,unkonwn)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:loose, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-normal.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (normal,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unkonwn, and line-break:normal, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unkonwn and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-hyphens-strict.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, linebreaks: hyphens (strict,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unkonwn, and line-break:strict, a browser will NOT allow a break before a hyphen.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `2010 HYPHEN
+2013 EN DASH
+301C WAVE DASH
+30A0 KATAKANA-HIRAGANA DOUBLE HYPHEN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-in-loose.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (loose,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:loose, a browser allows a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-in-normal.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (normal,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:normal, a browser will NOT allow a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-in-strict.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: IN (strict,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:strict, a browser will NOT allow a break before an inseparable character.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `2024  ONE DOT LEADER
+2025  TWO DOT LEADER
+2026  HORIZONTAL ELLIPSIS
+22EF  MIDLINE HORIZONTAL ELLIPSIS
+FE19  PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-iteration-loose.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (loose,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is uknown, and line-break:loose, a browser allows a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文文<br/>&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may appear at line start if lang unknown and loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-iteration-normal.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (normal,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:normal, a browser will NOT allow a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and normal');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-iteration-strict.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: iteration (strict,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:strict, a browser will NOT allow a break before an iteration mark.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: strict; }
+</style>
+<script>
+var charlist = `3005  IDEOGRAPHIC ITERATION MARK
+303B  VERTICAL IDEOGRAPHIC ITERATION MARK
+309D  HIRAGANA ITERATION MARK
+309E  HIRAGANA VOICED ITERATION MARK
+30FD  KATAKANA ITERATION MARK
+30FE  KATAKANA VOICED ITERATION MARK`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if lang unknown and strict');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-po-loose.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PO AFW (loose,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unknown, and line-break:loose, a browser will NOT allow a break before a PO character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: loose; }
+</style>
+<script>
+var charlist = `00B0  DEGREE SIGN
+2030  PER MILLE SIGN
+2032  PRIME
+2033  DOUBLE PRIME
+2035  REVERSED PRIME
+2103  DEGREE CELSIUS
+2109  DEGREE FAHRENHEIT
+FE6A  SMALL PERCENT SIGN
+FF05  FULLWIDTH PERCENT SIGN
+FFE0  FULLWIDTH CENT SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML = out
+setup({explicit_done: true});
+
+document.fonts.ready.then(validate);
+
+function validate() {
+  for (i=0;i<lines.length;i++) {
+    test(function() {
+      assert_true(spansNearEnough(i));
+    }, lines[i]+' may NOT appear at line start if loose');
+    // Hide successful tests.
+    if (spansNearEnough(i)) document.getElementById('test'+i).parentNode.style.display = 'none'
+  }
+  done();
+}
+</script>
+<!--Notes:
+The test creates a box with room for 6 characters, causing wrapping to occur either between the 6th and the 7th character, or before the 6th if the breaks after the 6th or before the 7th are prohibited.
+
+It also creates the expected behaviour with a ref instance, using <br/>. Each line ends with a span. The test then checks whether the left edge of the span is in the same place in test and ref instance.
+-->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/i18n/unknown-lang/css-text-line-break-po-normal.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>CSS text, linebreaks: PO AFW (normal,unknown)</title>
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta name="assert" content="When the language is unkonwn, and line-break:normal, a browser will NOT allow a break before a PO character with East Asian Width of A, F, or W.">
+<style type="text/css">
+@font-face {
+    font-family: 'mplus-1p-regular';
+    src: url('/fonts/mplus-1p-regular.woff') format('woff');
+    }
+#wrapper { position: relative; }
+.test { color: red; }
+.test, .ref { font-size: 30px; font-family: mplus-1p-regular, sans-serif; width: 185px; padding: 0; border: 1px solid orange; line-height: 1em; }
+</style>
+<style>
+.test { line-break: normal; }
+</style>
+<script>
+var charlist = `00B0  DEGREE SIGN
+2030  PER MILLE SIGN
+2032  PRIME
+2033  DOUBLE PRIME
+2035  REVERSED PRIME
+2103  DEGREE CELSIUS
+2109  DEGREE FAHRENHEIT
+FE6A  SMALL PERCENT SIGN
+FF05  FULLWIDTH PERCENT SIGN
+FFE0  FULLWIDTH CENT SIGN`
+</script>
+</head>
+<body>
+<script>
+var lines = charlist.split('\n')
+var out = '<div id="log"></div>\n'
+for (var i=0;i<lines.length;i++) {
+	// get the data
+	var firstSpace = lines[i].indexOf(' ')
+	var hex = lines[i].substr(0,firstSpace)
+	var name = lines[i].substr(firstSpace)
+	// make a test
+	out +=  '<div class="wrapper"><div>'+hex+'</div>' +
+	'<div class="test" id="test'+i+'" lang="">文文文文文文&#x'+hex+';字<span id="testSpan'+i+'">字</span></div>' +
+	 '<div class="ref" id="ref'+i+'" lang="">文文文文文<br/>文&#x'+hex+';字<span id="refSpan'+i+'">字</span></div>' +
+	 '</div>'
+	}
+function spansNearEnough(counter) {
+  return Math.abs( document.getElementById('testSpan'+counter).getBoundingClientRect().left
+           - document.getElementById('refSpan'+counter).getBoundingClientRect().left ) < 1;
+}
+
+document.querySelector('body').innerHTML =