chromium/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/text/2d.text.measure.caret-position.tentative.html

<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<title>OffscreenCanvas test: 2d.text.measure.caret-position.tentative</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/html/canvas/resources/canvas-tests.js"></script>

<h1>2d.text.measure.caret-position.tentative</h1>

<script>

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }


  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and no-directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '0px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '0px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('0px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('ltr' == 'ltr' && 'left' == 'end') ||
             ('ltr' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('left' == 'center') {
      offset -= width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('left' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('left' == 'right' ||
             ('rtl' == 'ltr' && 'left' == 'end') ||
             ('rtl' == 'rtl' && 'left' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'left';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('ltr' == 'ltr' && 'center' == 'end') ||
             ('ltr' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('center' == 'center') {
      offset -= width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('center' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('center' == 'right' ||
             ('rtl' == 'ltr' && 'center' == 'end') ||
             ('rtl' == 'rtl' && 'center' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'center';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('ltr' == 'ltr' && 'right' == 'end') ||
             ('ltr' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('right' == 'center') {
      offset -= width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('right' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('right' == 'right' ||
             ('rtl' == 'ltr' && 'right' == 'end') ||
             ('rtl' == 'rtl' && 'right' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'right';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('ltr' == 'ltr' && 'start' == 'end') ||
             ('ltr' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('start' == 'center') {
      offset -= width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('start' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('start' == 'right' ||
             ('rtl' == 'ltr' && 'start' == 'end') ||
             ('rtl' == 'rtl' && 'start' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'start';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'ltr';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('ltr' == 'ltr' && 'end' == 'end') ||
             ('ltr' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'ltr';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and directional-override.");

test(t => {
  const canvas = new OffscreenCanvas(100, 50);
  const ctx = canvas.getContext('2d');

  function alignOffset(offset, width) {
    if ('end' == 'center') {
      offset -= width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      offset -= width;
    }
    return offset;
  }

  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
    const LTR_OVERRIDE = '\u202D';
    const RTL_OVERRIDE = '\u202E';
    const OVERRIDE_END = '\u202C';
    if (direction_is_ltr) {
      return LTR_OVERRIDE + text + OVERRIDE_END;
    }
    return RTL_OVERRIDE + text + OVERRIDE_END;
  }

  function placeAndMeasureTextInDOM(text, text_width, point) {
    const el = document.createElement("p");
    el.innerHTML = text;
    el.style.font = '50px sans-serif';
    el.style.direction = 'rtl';
    el.style.letterSpacing = '10px';
    // Put the text top left to make offsets simpler.
    el.style.padding = '0px';
    el.style.margin = '0px';
    el.style.position = 'absolute';
    el.style.x = '0px';
    el.style.y = '0px';
    document.body.appendChild(el);
    text_bound = el.getBoundingClientRect();
    text_x = text_bound.x;
    text_y = text_bound.y + text_bound.height / 2;

    // Offset to the requested point determined by textAlign and direction.
    let text_align_dx = 0;
    if ('end' == 'center') {
      text_align_dx = text_width / 2;
    } else if ('end' == 'right' ||
             ('rtl' == 'ltr' && 'end' == 'end') ||
             ('rtl' == 'rtl' && 'end' == 'start')) {
      text_align_dx = text_width;
    }
    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");

    document.body.removeChild(el);

    return position.offset;
  }

  ctx.font = '50px sans-serif';
  ctx.direction = 'rtl';
  ctx.textAlign = 'end';
  ctx.letterSpacing = '10px';

  const kTexts = [
    'UNAVAILABLE',
    '🏁🎢🏁',
    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    '--abcd__'
  ]

  for (text of kTexts) {
    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
    const tm = ctx.measureText(text);
    text_width = tm.width;
    step = 30;
    if ('10px' == '10px') {
      step = 40;
    }

    offset = step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width / 2;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);

    offset = text_width - step;
    adjusted_offset = alignOffset(offset, text_width);
    tm_position = tm.caretPositionFromPoint(adjusted_offset);
    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    assert_equals(tm_position,
                  doc_position,
                  "for " + text + " offset " + offset);
  }
}, "Check that TextMetrics::caretPositionFromPoint() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and directional-override.");

</script>