tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

2d.text.measure.index-from-offset.tentative.html (154691B)


      1 <!DOCTYPE html>
      2 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
      3 <meta charset="UTF-8">
      4 <title>Canvas test: 2d.text.measure.index-from-offset.tentative</title>
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/html/canvas/resources/canvas-tests.js"></script>
      8 <link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
      9 
     10 <h1>2d.text.measure.index-from-offset.tentative</h1>
     11 
     12 <script>
     13 
     14 test(t => {
     15  const canvas = document.createElement('canvas');
     16  canvas.width = 100;
     17  canvas.height = 50;
     18  const ctx = canvas.getContext('2d');
     19 
     20  function alignOffset(offset, width) {
     21    if ('left' == 'center') {
     22      offset -= width / 2;
     23    } else if ('left' == 'right' ||
     24             ('ltr' == 'ltr' && 'left' == 'end') ||
     25             ('ltr' == 'rtl' && 'left' == 'start')) {
     26      offset -= width;
     27    }
     28    return offset;
     29  }
     30 
     31 
     32  function placeAndMeasureTextInDOM(text, text_width, point) {
     33    const el = document.createElement("p");
     34    el.innerHTML = text;
     35    el.style.font = '50px sans-serif';
     36    el.style.direction = 'ltr';
     37    el.style.letterSpacing = '0px';
     38    // Put the text top left to make offsets simpler.
     39    el.style.padding = '0px';
     40    el.style.margin = '0px';
     41    el.style.position = 'absolute';
     42    el.style.x = '0px';
     43    el.style.y = '0px';
     44    document.body.appendChild(el);
     45    text_bound = el.getBoundingClientRect();
     46    text_x = text_bound.x;
     47    text_y = text_bound.y + text_bound.height / 2;
     48 
     49    // Offset to the requested point determined by textAlign and direction.
     50    let text_align_dx = 0;
     51    if ('left' == 'center') {
     52      text_align_dx = text_width / 2;
     53    } else if ('left' == 'right' ||
     54             ('ltr' == 'ltr' && 'left' == 'end') ||
     55             ('ltr' == 'rtl' && 'left' == 'start')) {
     56      text_align_dx = text_width;
     57    }
     58    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
     59    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
     60 
     61    document.body.removeChild(el);
     62 
     63    return position.offset;
     64  }
     65 
     66  ctx.font = '50px sans-serif';
     67  ctx.direction = 'ltr';
     68  ctx.textAlign = 'left';
     69  ctx.letterSpacing = '0px';
     70 
     71  const kTexts = [
     72    'UNAVAILABLE',
     73    '🏁🎢🏁',
     74    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
     75    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
     76    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
     77    '--abcd__'
     78  ]
     79 
     80  for (text of kTexts) {
     81    const tm = ctx.measureText(text);
     82    text_width = tm.width;
     83    step = 30;
     84    if ('0px' == '10px') {
     85      step = 40;
     86    }
     87 
     88    offset = step;
     89    adjusted_offset = alignOffset(offset, text_width);
     90    tm_position = tm.getIndexFromOffset(adjusted_offset);
     91    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
     92    assert_equals(tm_position,
     93                  doc_position,
     94                  "for " + text + " offset " + offset);
     95 
     96    offset = text_width / 2 - 10;
     97    adjusted_offset = alignOffset(offset, text_width);
     98    tm_position = tm.getIndexFromOffset(adjusted_offset);
     99    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    100    assert_equals(tm_position,
    101                  doc_position,
    102                  "for " + text + " offset " + offset);
    103 
    104    offset = text_width / 2 + 10;
    105    adjusted_offset = alignOffset(offset, text_width);
    106    tm_position = tm.getIndexFromOffset(adjusted_offset);
    107    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    108    assert_equals(tm_position,
    109                  doc_position,
    110                  "for " + text + " offset " + offset);
    111 
    112    offset = text_width - step;
    113    adjusted_offset = alignOffset(offset, text_width);
    114    tm_position = tm.getIndexFromOffset(adjusted_offset);
    115    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    116    assert_equals(tm_position,
    117                  doc_position,
    118                  "for " + text + " offset " + offset);
    119  }
    120 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and no-directional-override.");
    121 
    122 test(t => {
    123  const canvas = document.createElement('canvas');
    124  canvas.width = 100;
    125  canvas.height = 50;
    126  const ctx = canvas.getContext('2d');
    127 
    128  function alignOffset(offset, width) {
    129    if ('left' == 'center') {
    130      offset -= width / 2;
    131    } else if ('left' == 'right' ||
    132             ('rtl' == 'ltr' && 'left' == 'end') ||
    133             ('rtl' == 'rtl' && 'left' == 'start')) {
    134      offset -= width;
    135    }
    136    return offset;
    137  }
    138 
    139 
    140  function placeAndMeasureTextInDOM(text, text_width, point) {
    141    const el = document.createElement("p");
    142    el.innerHTML = text;
    143    el.style.font = '50px sans-serif';
    144    el.style.direction = 'rtl';
    145    el.style.letterSpacing = '0px';
    146    // Put the text top left to make offsets simpler.
    147    el.style.padding = '0px';
    148    el.style.margin = '0px';
    149    el.style.position = 'absolute';
    150    el.style.x = '0px';
    151    el.style.y = '0px';
    152    document.body.appendChild(el);
    153    text_bound = el.getBoundingClientRect();
    154    text_x = text_bound.x;
    155    text_y = text_bound.y + text_bound.height / 2;
    156 
    157    // Offset to the requested point determined by textAlign and direction.
    158    let text_align_dx = 0;
    159    if ('left' == 'center') {
    160      text_align_dx = text_width / 2;
    161    } else if ('left' == 'right' ||
    162             ('rtl' == 'ltr' && 'left' == 'end') ||
    163             ('rtl' == 'rtl' && 'left' == 'start')) {
    164      text_align_dx = text_width;
    165    }
    166    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    167    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    168 
    169    document.body.removeChild(el);
    170 
    171    return position.offset;
    172  }
    173 
    174  ctx.font = '50px sans-serif';
    175  ctx.direction = 'rtl';
    176  ctx.textAlign = 'left';
    177  ctx.letterSpacing = '0px';
    178 
    179  const kTexts = [
    180    'UNAVAILABLE',
    181    '🏁🎢🏁',
    182    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    183    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    184    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    185    '--abcd__'
    186  ]
    187 
    188  for (text of kTexts) {
    189    const tm = ctx.measureText(text);
    190    text_width = tm.width;
    191    step = 30;
    192    if ('0px' == '10px') {
    193      step = 40;
    194    }
    195 
    196    offset = step;
    197    adjusted_offset = alignOffset(offset, text_width);
    198    tm_position = tm.getIndexFromOffset(adjusted_offset);
    199    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    200    assert_equals(tm_position,
    201                  doc_position,
    202                  "for " + text + " offset " + offset);
    203 
    204    offset = text_width / 2 - 10;
    205    adjusted_offset = alignOffset(offset, text_width);
    206    tm_position = tm.getIndexFromOffset(adjusted_offset);
    207    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    208    assert_equals(tm_position,
    209                  doc_position,
    210                  "for " + text + " offset " + offset);
    211 
    212    offset = text_width / 2 + 10;
    213    adjusted_offset = alignOffset(offset, text_width);
    214    tm_position = tm.getIndexFromOffset(adjusted_offset);
    215    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    216    assert_equals(tm_position,
    217                  doc_position,
    218                  "for " + text + " offset " + offset);
    219 
    220    offset = text_width - step;
    221    adjusted_offset = alignOffset(offset, text_width);
    222    tm_position = tm.getIndexFromOffset(adjusted_offset);
    223    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    224    assert_equals(tm_position,
    225                  doc_position,
    226                  "for " + text + " offset " + offset);
    227  }
    228 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and no-directional-override.");
    229 
    230 test(t => {
    231  const canvas = document.createElement('canvas');
    232  canvas.width = 100;
    233  canvas.height = 50;
    234  const ctx = canvas.getContext('2d');
    235 
    236  function alignOffset(offset, width) {
    237    if ('center' == 'center') {
    238      offset -= width / 2;
    239    } else if ('center' == 'right' ||
    240             ('ltr' == 'ltr' && 'center' == 'end') ||
    241             ('ltr' == 'rtl' && 'center' == 'start')) {
    242      offset -= width;
    243    }
    244    return offset;
    245  }
    246 
    247 
    248  function placeAndMeasureTextInDOM(text, text_width, point) {
    249    const el = document.createElement("p");
    250    el.innerHTML = text;
    251    el.style.font = '50px sans-serif';
    252    el.style.direction = 'ltr';
    253    el.style.letterSpacing = '0px';
    254    // Put the text top left to make offsets simpler.
    255    el.style.padding = '0px';
    256    el.style.margin = '0px';
    257    el.style.position = 'absolute';
    258    el.style.x = '0px';
    259    el.style.y = '0px';
    260    document.body.appendChild(el);
    261    text_bound = el.getBoundingClientRect();
    262    text_x = text_bound.x;
    263    text_y = text_bound.y + text_bound.height / 2;
    264 
    265    // Offset to the requested point determined by textAlign and direction.
    266    let text_align_dx = 0;
    267    if ('center' == 'center') {
    268      text_align_dx = text_width / 2;
    269    } else if ('center' == 'right' ||
    270             ('ltr' == 'ltr' && 'center' == 'end') ||
    271             ('ltr' == 'rtl' && 'center' == 'start')) {
    272      text_align_dx = text_width;
    273    }
    274    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    275    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    276 
    277    document.body.removeChild(el);
    278 
    279    return position.offset;
    280  }
    281 
    282  ctx.font = '50px sans-serif';
    283  ctx.direction = 'ltr';
    284  ctx.textAlign = 'center';
    285  ctx.letterSpacing = '0px';
    286 
    287  const kTexts = [
    288    'UNAVAILABLE',
    289    '🏁🎢🏁',
    290    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    291    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    292    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    293    '--abcd__'
    294  ]
    295 
    296  for (text of kTexts) {
    297    const tm = ctx.measureText(text);
    298    text_width = tm.width;
    299    step = 30;
    300    if ('0px' == '10px') {
    301      step = 40;
    302    }
    303 
    304    offset = step;
    305    adjusted_offset = alignOffset(offset, text_width);
    306    tm_position = tm.getIndexFromOffset(adjusted_offset);
    307    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    308    assert_equals(tm_position,
    309                  doc_position,
    310                  "for " + text + " offset " + offset);
    311 
    312    offset = text_width / 2 - 10;
    313    adjusted_offset = alignOffset(offset, text_width);
    314    tm_position = tm.getIndexFromOffset(adjusted_offset);
    315    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    316    assert_equals(tm_position,
    317                  doc_position,
    318                  "for " + text + " offset " + offset);
    319 
    320    offset = text_width / 2 + 10;
    321    adjusted_offset = alignOffset(offset, text_width);
    322    tm_position = tm.getIndexFromOffset(adjusted_offset);
    323    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    324    assert_equals(tm_position,
    325                  doc_position,
    326                  "for " + text + " offset " + offset);
    327 
    328    offset = text_width - step;
    329    adjusted_offset = alignOffset(offset, text_width);
    330    tm_position = tm.getIndexFromOffset(adjusted_offset);
    331    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    332    assert_equals(tm_position,
    333                  doc_position,
    334                  "for " + text + " offset " + offset);
    335  }
    336 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and no-directional-override.");
    337 
    338 test(t => {
    339  const canvas = document.createElement('canvas');
    340  canvas.width = 100;
    341  canvas.height = 50;
    342  const ctx = canvas.getContext('2d');
    343 
    344  function alignOffset(offset, width) {
    345    if ('center' == 'center') {
    346      offset -= width / 2;
    347    } else if ('center' == 'right' ||
    348             ('rtl' == 'ltr' && 'center' == 'end') ||
    349             ('rtl' == 'rtl' && 'center' == 'start')) {
    350      offset -= width;
    351    }
    352    return offset;
    353  }
    354 
    355 
    356  function placeAndMeasureTextInDOM(text, text_width, point) {
    357    const el = document.createElement("p");
    358    el.innerHTML = text;
    359    el.style.font = '50px sans-serif';
    360    el.style.direction = 'rtl';
    361    el.style.letterSpacing = '0px';
    362    // Put the text top left to make offsets simpler.
    363    el.style.padding = '0px';
    364    el.style.margin = '0px';
    365    el.style.position = 'absolute';
    366    el.style.x = '0px';
    367    el.style.y = '0px';
    368    document.body.appendChild(el);
    369    text_bound = el.getBoundingClientRect();
    370    text_x = text_bound.x;
    371    text_y = text_bound.y + text_bound.height / 2;
    372 
    373    // Offset to the requested point determined by textAlign and direction.
    374    let text_align_dx = 0;
    375    if ('center' == 'center') {
    376      text_align_dx = text_width / 2;
    377    } else if ('center' == 'right' ||
    378             ('rtl' == 'ltr' && 'center' == 'end') ||
    379             ('rtl' == 'rtl' && 'center' == 'start')) {
    380      text_align_dx = text_width;
    381    }
    382    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    383    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    384 
    385    document.body.removeChild(el);
    386 
    387    return position.offset;
    388  }
    389 
    390  ctx.font = '50px sans-serif';
    391  ctx.direction = 'rtl';
    392  ctx.textAlign = 'center';
    393  ctx.letterSpacing = '0px';
    394 
    395  const kTexts = [
    396    'UNAVAILABLE',
    397    '🏁🎢🏁',
    398    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    399    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    400    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    401    '--abcd__'
    402  ]
    403 
    404  for (text of kTexts) {
    405    const tm = ctx.measureText(text);
    406    text_width = tm.width;
    407    step = 30;
    408    if ('0px' == '10px') {
    409      step = 40;
    410    }
    411 
    412    offset = step;
    413    adjusted_offset = alignOffset(offset, text_width);
    414    tm_position = tm.getIndexFromOffset(adjusted_offset);
    415    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    416    assert_equals(tm_position,
    417                  doc_position,
    418                  "for " + text + " offset " + offset);
    419 
    420    offset = text_width / 2 - 10;
    421    adjusted_offset = alignOffset(offset, text_width);
    422    tm_position = tm.getIndexFromOffset(adjusted_offset);
    423    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    424    assert_equals(tm_position,
    425                  doc_position,
    426                  "for " + text + " offset " + offset);
    427 
    428    offset = text_width / 2 + 10;
    429    adjusted_offset = alignOffset(offset, text_width);
    430    tm_position = tm.getIndexFromOffset(adjusted_offset);
    431    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    432    assert_equals(tm_position,
    433                  doc_position,
    434                  "for " + text + " offset " + offset);
    435 
    436    offset = text_width - step;
    437    adjusted_offset = alignOffset(offset, text_width);
    438    tm_position = tm.getIndexFromOffset(adjusted_offset);
    439    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    440    assert_equals(tm_position,
    441                  doc_position,
    442                  "for " + text + " offset " + offset);
    443  }
    444 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and no-directional-override.");
    445 
    446 test(t => {
    447  const canvas = document.createElement('canvas');
    448  canvas.width = 100;
    449  canvas.height = 50;
    450  const ctx = canvas.getContext('2d');
    451 
    452  function alignOffset(offset, width) {
    453    if ('right' == 'center') {
    454      offset -= width / 2;
    455    } else if ('right' == 'right' ||
    456             ('ltr' == 'ltr' && 'right' == 'end') ||
    457             ('ltr' == 'rtl' && 'right' == 'start')) {
    458      offset -= width;
    459    }
    460    return offset;
    461  }
    462 
    463 
    464  function placeAndMeasureTextInDOM(text, text_width, point) {
    465    const el = document.createElement("p");
    466    el.innerHTML = text;
    467    el.style.font = '50px sans-serif';
    468    el.style.direction = 'ltr';
    469    el.style.letterSpacing = '0px';
    470    // Put the text top left to make offsets simpler.
    471    el.style.padding = '0px';
    472    el.style.margin = '0px';
    473    el.style.position = 'absolute';
    474    el.style.x = '0px';
    475    el.style.y = '0px';
    476    document.body.appendChild(el);
    477    text_bound = el.getBoundingClientRect();
    478    text_x = text_bound.x;
    479    text_y = text_bound.y + text_bound.height / 2;
    480 
    481    // Offset to the requested point determined by textAlign and direction.
    482    let text_align_dx = 0;
    483    if ('right' == 'center') {
    484      text_align_dx = text_width / 2;
    485    } else if ('right' == 'right' ||
    486             ('ltr' == 'ltr' && 'right' == 'end') ||
    487             ('ltr' == 'rtl' && 'right' == 'start')) {
    488      text_align_dx = text_width;
    489    }
    490    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    491    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    492 
    493    document.body.removeChild(el);
    494 
    495    return position.offset;
    496  }
    497 
    498  ctx.font = '50px sans-serif';
    499  ctx.direction = 'ltr';
    500  ctx.textAlign = 'right';
    501  ctx.letterSpacing = '0px';
    502 
    503  const kTexts = [
    504    'UNAVAILABLE',
    505    '🏁🎢🏁',
    506    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    507    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    508    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    509    '--abcd__'
    510  ]
    511 
    512  for (text of kTexts) {
    513    const tm = ctx.measureText(text);
    514    text_width = tm.width;
    515    step = 30;
    516    if ('0px' == '10px') {
    517      step = 40;
    518    }
    519 
    520    offset = step;
    521    adjusted_offset = alignOffset(offset, text_width);
    522    tm_position = tm.getIndexFromOffset(adjusted_offset);
    523    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    524    assert_equals(tm_position,
    525                  doc_position,
    526                  "for " + text + " offset " + offset);
    527 
    528    offset = text_width / 2 - 10;
    529    adjusted_offset = alignOffset(offset, text_width);
    530    tm_position = tm.getIndexFromOffset(adjusted_offset);
    531    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    532    assert_equals(tm_position,
    533                  doc_position,
    534                  "for " + text + " offset " + offset);
    535 
    536    offset = text_width / 2 + 10;
    537    adjusted_offset = alignOffset(offset, text_width);
    538    tm_position = tm.getIndexFromOffset(adjusted_offset);
    539    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    540    assert_equals(tm_position,
    541                  doc_position,
    542                  "for " + text + " offset " + offset);
    543 
    544    offset = text_width - step;
    545    adjusted_offset = alignOffset(offset, text_width);
    546    tm_position = tm.getIndexFromOffset(adjusted_offset);
    547    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    548    assert_equals(tm_position,
    549                  doc_position,
    550                  "for " + text + " offset " + offset);
    551  }
    552 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and no-directional-override.");
    553 
    554 test(t => {
    555  const canvas = document.createElement('canvas');
    556  canvas.width = 100;
    557  canvas.height = 50;
    558  const ctx = canvas.getContext('2d');
    559 
    560  function alignOffset(offset, width) {
    561    if ('right' == 'center') {
    562      offset -= width / 2;
    563    } else if ('right' == 'right' ||
    564             ('rtl' == 'ltr' && 'right' == 'end') ||
    565             ('rtl' == 'rtl' && 'right' == 'start')) {
    566      offset -= width;
    567    }
    568    return offset;
    569  }
    570 
    571 
    572  function placeAndMeasureTextInDOM(text, text_width, point) {
    573    const el = document.createElement("p");
    574    el.innerHTML = text;
    575    el.style.font = '50px sans-serif';
    576    el.style.direction = 'rtl';
    577    el.style.letterSpacing = '0px';
    578    // Put the text top left to make offsets simpler.
    579    el.style.padding = '0px';
    580    el.style.margin = '0px';
    581    el.style.position = 'absolute';
    582    el.style.x = '0px';
    583    el.style.y = '0px';
    584    document.body.appendChild(el);
    585    text_bound = el.getBoundingClientRect();
    586    text_x = text_bound.x;
    587    text_y = text_bound.y + text_bound.height / 2;
    588 
    589    // Offset to the requested point determined by textAlign and direction.
    590    let text_align_dx = 0;
    591    if ('right' == 'center') {
    592      text_align_dx = text_width / 2;
    593    } else if ('right' == 'right' ||
    594             ('rtl' == 'ltr' && 'right' == 'end') ||
    595             ('rtl' == 'rtl' && 'right' == 'start')) {
    596      text_align_dx = text_width;
    597    }
    598    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    599    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    600 
    601    document.body.removeChild(el);
    602 
    603    return position.offset;
    604  }
    605 
    606  ctx.font = '50px sans-serif';
    607  ctx.direction = 'rtl';
    608  ctx.textAlign = 'right';
    609  ctx.letterSpacing = '0px';
    610 
    611  const kTexts = [
    612    'UNAVAILABLE',
    613    '🏁🎢🏁',
    614    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    615    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    616    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    617    '--abcd__'
    618  ]
    619 
    620  for (text of kTexts) {
    621    const tm = ctx.measureText(text);
    622    text_width = tm.width;
    623    step = 30;
    624    if ('0px' == '10px') {
    625      step = 40;
    626    }
    627 
    628    offset = step;
    629    adjusted_offset = alignOffset(offset, text_width);
    630    tm_position = tm.getIndexFromOffset(adjusted_offset);
    631    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    632    assert_equals(tm_position,
    633                  doc_position,
    634                  "for " + text + " offset " + offset);
    635 
    636    offset = text_width / 2 - 10;
    637    adjusted_offset = alignOffset(offset, text_width);
    638    tm_position = tm.getIndexFromOffset(adjusted_offset);
    639    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    640    assert_equals(tm_position,
    641                  doc_position,
    642                  "for " + text + " offset " + offset);
    643 
    644    offset = text_width / 2 + 10;
    645    adjusted_offset = alignOffset(offset, text_width);
    646    tm_position = tm.getIndexFromOffset(adjusted_offset);
    647    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    648    assert_equals(tm_position,
    649                  doc_position,
    650                  "for " + text + " offset " + offset);
    651 
    652    offset = text_width - step;
    653    adjusted_offset = alignOffset(offset, text_width);
    654    tm_position = tm.getIndexFromOffset(adjusted_offset);
    655    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    656    assert_equals(tm_position,
    657                  doc_position,
    658                  "for " + text + " offset " + offset);
    659  }
    660 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and no-directional-override.");
    661 
    662 test(t => {
    663  const canvas = document.createElement('canvas');
    664  canvas.width = 100;
    665  canvas.height = 50;
    666  const ctx = canvas.getContext('2d');
    667 
    668  function alignOffset(offset, width) {
    669    if ('start' == 'center') {
    670      offset -= width / 2;
    671    } else if ('start' == 'right' ||
    672             ('ltr' == 'ltr' && 'start' == 'end') ||
    673             ('ltr' == 'rtl' && 'start' == 'start')) {
    674      offset -= width;
    675    }
    676    return offset;
    677  }
    678 
    679 
    680  function placeAndMeasureTextInDOM(text, text_width, point) {
    681    const el = document.createElement("p");
    682    el.innerHTML = text;
    683    el.style.font = '50px sans-serif';
    684    el.style.direction = 'ltr';
    685    el.style.letterSpacing = '0px';
    686    // Put the text top left to make offsets simpler.
    687    el.style.padding = '0px';
    688    el.style.margin = '0px';
    689    el.style.position = 'absolute';
    690    el.style.x = '0px';
    691    el.style.y = '0px';
    692    document.body.appendChild(el);
    693    text_bound = el.getBoundingClientRect();
    694    text_x = text_bound.x;
    695    text_y = text_bound.y + text_bound.height / 2;
    696 
    697    // Offset to the requested point determined by textAlign and direction.
    698    let text_align_dx = 0;
    699    if ('start' == 'center') {
    700      text_align_dx = text_width / 2;
    701    } else if ('start' == 'right' ||
    702             ('ltr' == 'ltr' && 'start' == 'end') ||
    703             ('ltr' == 'rtl' && 'start' == 'start')) {
    704      text_align_dx = text_width;
    705    }
    706    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    707    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    708 
    709    document.body.removeChild(el);
    710 
    711    return position.offset;
    712  }
    713 
    714  ctx.font = '50px sans-serif';
    715  ctx.direction = 'ltr';
    716  ctx.textAlign = 'start';
    717  ctx.letterSpacing = '0px';
    718 
    719  const kTexts = [
    720    'UNAVAILABLE',
    721    '🏁🎢🏁',
    722    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    723    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    724    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    725    '--abcd__'
    726  ]
    727 
    728  for (text of kTexts) {
    729    const tm = ctx.measureText(text);
    730    text_width = tm.width;
    731    step = 30;
    732    if ('0px' == '10px') {
    733      step = 40;
    734    }
    735 
    736    offset = step;
    737    adjusted_offset = alignOffset(offset, text_width);
    738    tm_position = tm.getIndexFromOffset(adjusted_offset);
    739    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    740    assert_equals(tm_position,
    741                  doc_position,
    742                  "for " + text + " offset " + offset);
    743 
    744    offset = text_width / 2 - 10;
    745    adjusted_offset = alignOffset(offset, text_width);
    746    tm_position = tm.getIndexFromOffset(adjusted_offset);
    747    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    748    assert_equals(tm_position,
    749                  doc_position,
    750                  "for " + text + " offset " + offset);
    751 
    752    offset = text_width / 2 + 10;
    753    adjusted_offset = alignOffset(offset, text_width);
    754    tm_position = tm.getIndexFromOffset(adjusted_offset);
    755    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    756    assert_equals(tm_position,
    757                  doc_position,
    758                  "for " + text + " offset " + offset);
    759 
    760    offset = text_width - step;
    761    adjusted_offset = alignOffset(offset, text_width);
    762    tm_position = tm.getIndexFromOffset(adjusted_offset);
    763    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    764    assert_equals(tm_position,
    765                  doc_position,
    766                  "for " + text + " offset " + offset);
    767  }
    768 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and no-directional-override.");
    769 
    770 test(t => {
    771  const canvas = document.createElement('canvas');
    772  canvas.width = 100;
    773  canvas.height = 50;
    774  const ctx = canvas.getContext('2d');
    775 
    776  function alignOffset(offset, width) {
    777    if ('start' == 'center') {
    778      offset -= width / 2;
    779    } else if ('start' == 'right' ||
    780             ('rtl' == 'ltr' && 'start' == 'end') ||
    781             ('rtl' == 'rtl' && 'start' == 'start')) {
    782      offset -= width;
    783    }
    784    return offset;
    785  }
    786 
    787 
    788  function placeAndMeasureTextInDOM(text, text_width, point) {
    789    const el = document.createElement("p");
    790    el.innerHTML = text;
    791    el.style.font = '50px sans-serif';
    792    el.style.direction = 'rtl';
    793    el.style.letterSpacing = '0px';
    794    // Put the text top left to make offsets simpler.
    795    el.style.padding = '0px';
    796    el.style.margin = '0px';
    797    el.style.position = 'absolute';
    798    el.style.x = '0px';
    799    el.style.y = '0px';
    800    document.body.appendChild(el);
    801    text_bound = el.getBoundingClientRect();
    802    text_x = text_bound.x;
    803    text_y = text_bound.y + text_bound.height / 2;
    804 
    805    // Offset to the requested point determined by textAlign and direction.
    806    let text_align_dx = 0;
    807    if ('start' == 'center') {
    808      text_align_dx = text_width / 2;
    809    } else if ('start' == 'right' ||
    810             ('rtl' == 'ltr' && 'start' == 'end') ||
    811             ('rtl' == 'rtl' && 'start' == 'start')) {
    812      text_align_dx = text_width;
    813    }
    814    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    815    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    816 
    817    document.body.removeChild(el);
    818 
    819    return position.offset;
    820  }
    821 
    822  ctx.font = '50px sans-serif';
    823  ctx.direction = 'rtl';
    824  ctx.textAlign = 'start';
    825  ctx.letterSpacing = '0px';
    826 
    827  const kTexts = [
    828    'UNAVAILABLE',
    829    '🏁🎢🏁',
    830    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    831    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    832    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    833    '--abcd__'
    834  ]
    835 
    836  for (text of kTexts) {
    837    const tm = ctx.measureText(text);
    838    text_width = tm.width;
    839    step = 30;
    840    if ('0px' == '10px') {
    841      step = 40;
    842    }
    843 
    844    offset = step;
    845    adjusted_offset = alignOffset(offset, text_width);
    846    tm_position = tm.getIndexFromOffset(adjusted_offset);
    847    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    848    assert_equals(tm_position,
    849                  doc_position,
    850                  "for " + text + " offset " + offset);
    851 
    852    offset = text_width / 2 - 10;
    853    adjusted_offset = alignOffset(offset, text_width);
    854    tm_position = tm.getIndexFromOffset(adjusted_offset);
    855    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    856    assert_equals(tm_position,
    857                  doc_position,
    858                  "for " + text + " offset " + offset);
    859 
    860    offset = text_width / 2 + 10;
    861    adjusted_offset = alignOffset(offset, text_width);
    862    tm_position = tm.getIndexFromOffset(adjusted_offset);
    863    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    864    assert_equals(tm_position,
    865                  doc_position,
    866                  "for " + text + " offset " + offset);
    867 
    868    offset = text_width - step;
    869    adjusted_offset = alignOffset(offset, text_width);
    870    tm_position = tm.getIndexFromOffset(adjusted_offset);
    871    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    872    assert_equals(tm_position,
    873                  doc_position,
    874                  "for " + text + " offset " + offset);
    875  }
    876 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and no-directional-override.");
    877 
    878 test(t => {
    879  const canvas = document.createElement('canvas');
    880  canvas.width = 100;
    881  canvas.height = 50;
    882  const ctx = canvas.getContext('2d');
    883 
    884  function alignOffset(offset, width) {
    885    if ('end' == 'center') {
    886      offset -= width / 2;
    887    } else if ('end' == 'right' ||
    888             ('ltr' == 'ltr' && 'end' == 'end') ||
    889             ('ltr' == 'rtl' && 'end' == 'start')) {
    890      offset -= width;
    891    }
    892    return offset;
    893  }
    894 
    895 
    896  function placeAndMeasureTextInDOM(text, text_width, point) {
    897    const el = document.createElement("p");
    898    el.innerHTML = text;
    899    el.style.font = '50px sans-serif';
    900    el.style.direction = 'ltr';
    901    el.style.letterSpacing = '0px';
    902    // Put the text top left to make offsets simpler.
    903    el.style.padding = '0px';
    904    el.style.margin = '0px';
    905    el.style.position = 'absolute';
    906    el.style.x = '0px';
    907    el.style.y = '0px';
    908    document.body.appendChild(el);
    909    text_bound = el.getBoundingClientRect();
    910    text_x = text_bound.x;
    911    text_y = text_bound.y + text_bound.height / 2;
    912 
    913    // Offset to the requested point determined by textAlign and direction.
    914    let text_align_dx = 0;
    915    if ('end' == 'center') {
    916      text_align_dx = text_width / 2;
    917    } else if ('end' == 'right' ||
    918             ('ltr' == 'ltr' && 'end' == 'end') ||
    919             ('ltr' == 'rtl' && 'end' == 'start')) {
    920      text_align_dx = text_width;
    921    }
    922    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
    923    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
    924 
    925    document.body.removeChild(el);
    926 
    927    return position.offset;
    928  }
    929 
    930  ctx.font = '50px sans-serif';
    931  ctx.direction = 'ltr';
    932  ctx.textAlign = 'end';
    933  ctx.letterSpacing = '0px';
    934 
    935  const kTexts = [
    936    'UNAVAILABLE',
    937    '🏁🎢🏁',
    938    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
    939    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
    940    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
    941    '--abcd__'
    942  ]
    943 
    944  for (text of kTexts) {
    945    const tm = ctx.measureText(text);
    946    text_width = tm.width;
    947    step = 30;
    948    if ('0px' == '10px') {
    949      step = 40;
    950    }
    951 
    952    offset = step;
    953    adjusted_offset = alignOffset(offset, text_width);
    954    tm_position = tm.getIndexFromOffset(adjusted_offset);
    955    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    956    assert_equals(tm_position,
    957                  doc_position,
    958                  "for " + text + " offset " + offset);
    959 
    960    offset = text_width / 2 - 10;
    961    adjusted_offset = alignOffset(offset, text_width);
    962    tm_position = tm.getIndexFromOffset(adjusted_offset);
    963    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    964    assert_equals(tm_position,
    965                  doc_position,
    966                  "for " + text + " offset " + offset);
    967 
    968    offset = text_width / 2 + 10;
    969    adjusted_offset = alignOffset(offset, text_width);
    970    tm_position = tm.getIndexFromOffset(adjusted_offset);
    971    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    972    assert_equals(tm_position,
    973                  doc_position,
    974                  "for " + text + " offset " + offset);
    975 
    976    offset = text_width - step;
    977    adjusted_offset = alignOffset(offset, text_width);
    978    tm_position = tm.getIndexFromOffset(adjusted_offset);
    979    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
    980    assert_equals(tm_position,
    981                  doc_position,
    982                  "for " + text + " offset " + offset);
    983  }
    984 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and no-directional-override.");
    985 
    986 test(t => {
    987  const canvas = document.createElement('canvas');
    988  canvas.width = 100;
    989  canvas.height = 50;
    990  const ctx = canvas.getContext('2d');
    991 
    992  function alignOffset(offset, width) {
    993    if ('end' == 'center') {
    994      offset -= width / 2;
    995    } else if ('end' == 'right' ||
    996             ('rtl' == 'ltr' && 'end' == 'end') ||
    997             ('rtl' == 'rtl' && 'end' == 'start')) {
    998      offset -= width;
    999    }
   1000    return offset;
   1001  }
   1002 
   1003 
   1004  function placeAndMeasureTextInDOM(text, text_width, point) {
   1005    const el = document.createElement("p");
   1006    el.innerHTML = text;
   1007    el.style.font = '50px sans-serif';
   1008    el.style.direction = 'rtl';
   1009    el.style.letterSpacing = '0px';
   1010    // Put the text top left to make offsets simpler.
   1011    el.style.padding = '0px';
   1012    el.style.margin = '0px';
   1013    el.style.position = 'absolute';
   1014    el.style.x = '0px';
   1015    el.style.y = '0px';
   1016    document.body.appendChild(el);
   1017    text_bound = el.getBoundingClientRect();
   1018    text_x = text_bound.x;
   1019    text_y = text_bound.y + text_bound.height / 2;
   1020 
   1021    // Offset to the requested point determined by textAlign and direction.
   1022    let text_align_dx = 0;
   1023    if ('end' == 'center') {
   1024      text_align_dx = text_width / 2;
   1025    } else if ('end' == 'right' ||
   1026             ('rtl' == 'ltr' && 'end' == 'end') ||
   1027             ('rtl' == 'rtl' && 'end' == 'start')) {
   1028      text_align_dx = text_width;
   1029    }
   1030    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1031    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1032 
   1033    document.body.removeChild(el);
   1034 
   1035    return position.offset;
   1036  }
   1037 
   1038  ctx.font = '50px sans-serif';
   1039  ctx.direction = 'rtl';
   1040  ctx.textAlign = 'end';
   1041  ctx.letterSpacing = '0px';
   1042 
   1043  const kTexts = [
   1044    'UNAVAILABLE',
   1045    '🏁🎢🏁',
   1046    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1047    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1048    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1049    '--abcd__'
   1050  ]
   1051 
   1052  for (text of kTexts) {
   1053    const tm = ctx.measureText(text);
   1054    text_width = tm.width;
   1055    step = 30;
   1056    if ('0px' == '10px') {
   1057      step = 40;
   1058    }
   1059 
   1060    offset = step;
   1061    adjusted_offset = alignOffset(offset, text_width);
   1062    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1063    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1064    assert_equals(tm_position,
   1065                  doc_position,
   1066                  "for " + text + " offset " + offset);
   1067 
   1068    offset = text_width / 2 - 10;
   1069    adjusted_offset = alignOffset(offset, text_width);
   1070    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1071    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1072    assert_equals(tm_position,
   1073                  doc_position,
   1074                  "for " + text + " offset " + offset);
   1075 
   1076    offset = text_width / 2 + 10;
   1077    adjusted_offset = alignOffset(offset, text_width);
   1078    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1079    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1080    assert_equals(tm_position,
   1081                  doc_position,
   1082                  "for " + text + " offset " + offset);
   1083 
   1084    offset = text_width - step;
   1085    adjusted_offset = alignOffset(offset, text_width);
   1086    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1087    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1088    assert_equals(tm_position,
   1089                  doc_position,
   1090                  "for " + text + " offset " + offset);
   1091  }
   1092 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and no-directional-override.");
   1093 
   1094 test(t => {
   1095  const canvas = document.createElement('canvas');
   1096  canvas.width = 100;
   1097  canvas.height = 50;
   1098  const ctx = canvas.getContext('2d');
   1099 
   1100  function alignOffset(offset, width) {
   1101    if ('left' == 'center') {
   1102      offset -= width / 2;
   1103    } else if ('left' == 'right' ||
   1104             ('ltr' == 'ltr' && 'left' == 'end') ||
   1105             ('ltr' == 'rtl' && 'left' == 'start')) {
   1106      offset -= width;
   1107    }
   1108    return offset;
   1109  }
   1110 
   1111 
   1112  function placeAndMeasureTextInDOM(text, text_width, point) {
   1113    const el = document.createElement("p");
   1114    el.innerHTML = text;
   1115    el.style.font = '50px sans-serif';
   1116    el.style.direction = 'ltr';
   1117    el.style.letterSpacing = '10px';
   1118    // Put the text top left to make offsets simpler.
   1119    el.style.padding = '0px';
   1120    el.style.margin = '0px';
   1121    el.style.position = 'absolute';
   1122    el.style.x = '0px';
   1123    el.style.y = '0px';
   1124    document.body.appendChild(el);
   1125    text_bound = el.getBoundingClientRect();
   1126    text_x = text_bound.x;
   1127    text_y = text_bound.y + text_bound.height / 2;
   1128 
   1129    // Offset to the requested point determined by textAlign and direction.
   1130    let text_align_dx = 0;
   1131    if ('left' == 'center') {
   1132      text_align_dx = text_width / 2;
   1133    } else if ('left' == 'right' ||
   1134             ('ltr' == 'ltr' && 'left' == 'end') ||
   1135             ('ltr' == 'rtl' && 'left' == 'start')) {
   1136      text_align_dx = text_width;
   1137    }
   1138    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1139    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1140 
   1141    document.body.removeChild(el);
   1142 
   1143    return position.offset;
   1144  }
   1145 
   1146  ctx.font = '50px sans-serif';
   1147  ctx.direction = 'ltr';
   1148  ctx.textAlign = 'left';
   1149  ctx.letterSpacing = '10px';
   1150 
   1151  const kTexts = [
   1152    'UNAVAILABLE',
   1153    '🏁🎢🏁',
   1154    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1155    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1156    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1157    '--abcd__'
   1158  ]
   1159 
   1160  for (text of kTexts) {
   1161    const tm = ctx.measureText(text);
   1162    text_width = tm.width;
   1163    step = 30;
   1164    if ('10px' == '10px') {
   1165      step = 40;
   1166    }
   1167 
   1168    offset = step;
   1169    adjusted_offset = alignOffset(offset, text_width);
   1170    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1171    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1172    assert_equals(tm_position,
   1173                  doc_position,
   1174                  "for " + text + " offset " + offset);
   1175 
   1176    offset = text_width / 2 - 10;
   1177    adjusted_offset = alignOffset(offset, text_width);
   1178    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1179    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1180    assert_equals(tm_position,
   1181                  doc_position,
   1182                  "for " + text + " offset " + offset);
   1183 
   1184    offset = text_width / 2 + 10;
   1185    adjusted_offset = alignOffset(offset, text_width);
   1186    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1187    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1188    assert_equals(tm_position,
   1189                  doc_position,
   1190                  "for " + text + " offset " + offset);
   1191 
   1192    offset = text_width - step;
   1193    adjusted_offset = alignOffset(offset, text_width);
   1194    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1195    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1196    assert_equals(tm_position,
   1197                  doc_position,
   1198                  "for " + text + " offset " + offset);
   1199  }
   1200 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and no-directional-override.");
   1201 
   1202 test(t => {
   1203  const canvas = document.createElement('canvas');
   1204  canvas.width = 100;
   1205  canvas.height = 50;
   1206  const ctx = canvas.getContext('2d');
   1207 
   1208  function alignOffset(offset, width) {
   1209    if ('left' == 'center') {
   1210      offset -= width / 2;
   1211    } else if ('left' == 'right' ||
   1212             ('rtl' == 'ltr' && 'left' == 'end') ||
   1213             ('rtl' == 'rtl' && 'left' == 'start')) {
   1214      offset -= width;
   1215    }
   1216    return offset;
   1217  }
   1218 
   1219 
   1220  function placeAndMeasureTextInDOM(text, text_width, point) {
   1221    const el = document.createElement("p");
   1222    el.innerHTML = text;
   1223    el.style.font = '50px sans-serif';
   1224    el.style.direction = 'rtl';
   1225    el.style.letterSpacing = '10px';
   1226    // Put the text top left to make offsets simpler.
   1227    el.style.padding = '0px';
   1228    el.style.margin = '0px';
   1229    el.style.position = 'absolute';
   1230    el.style.x = '0px';
   1231    el.style.y = '0px';
   1232    document.body.appendChild(el);
   1233    text_bound = el.getBoundingClientRect();
   1234    text_x = text_bound.x;
   1235    text_y = text_bound.y + text_bound.height / 2;
   1236 
   1237    // Offset to the requested point determined by textAlign and direction.
   1238    let text_align_dx = 0;
   1239    if ('left' == 'center') {
   1240      text_align_dx = text_width / 2;
   1241    } else if ('left' == 'right' ||
   1242             ('rtl' == 'ltr' && 'left' == 'end') ||
   1243             ('rtl' == 'rtl' && 'left' == 'start')) {
   1244      text_align_dx = text_width;
   1245    }
   1246    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1247    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1248 
   1249    document.body.removeChild(el);
   1250 
   1251    return position.offset;
   1252  }
   1253 
   1254  ctx.font = '50px sans-serif';
   1255  ctx.direction = 'rtl';
   1256  ctx.textAlign = 'left';
   1257  ctx.letterSpacing = '10px';
   1258 
   1259  const kTexts = [
   1260    'UNAVAILABLE',
   1261    '🏁🎢🏁',
   1262    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1263    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1264    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1265    '--abcd__'
   1266  ]
   1267 
   1268  for (text of kTexts) {
   1269    const tm = ctx.measureText(text);
   1270    text_width = tm.width;
   1271    step = 30;
   1272    if ('10px' == '10px') {
   1273      step = 40;
   1274    }
   1275 
   1276    offset = step;
   1277    adjusted_offset = alignOffset(offset, text_width);
   1278    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1279    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1280    assert_equals(tm_position,
   1281                  doc_position,
   1282                  "for " + text + " offset " + offset);
   1283 
   1284    offset = text_width / 2 - 10;
   1285    adjusted_offset = alignOffset(offset, text_width);
   1286    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1287    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1288    assert_equals(tm_position,
   1289                  doc_position,
   1290                  "for " + text + " offset " + offset);
   1291 
   1292    offset = text_width / 2 + 10;
   1293    adjusted_offset = alignOffset(offset, text_width);
   1294    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1295    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1296    assert_equals(tm_position,
   1297                  doc_position,
   1298                  "for " + text + " offset " + offset);
   1299 
   1300    offset = text_width - step;
   1301    adjusted_offset = alignOffset(offset, text_width);
   1302    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1303    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1304    assert_equals(tm_position,
   1305                  doc_position,
   1306                  "for " + text + " offset " + offset);
   1307  }
   1308 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and no-directional-override.");
   1309 
   1310 test(t => {
   1311  const canvas = document.createElement('canvas');
   1312  canvas.width = 100;
   1313  canvas.height = 50;
   1314  const ctx = canvas.getContext('2d');
   1315 
   1316  function alignOffset(offset, width) {
   1317    if ('center' == 'center') {
   1318      offset -= width / 2;
   1319    } else if ('center' == 'right' ||
   1320             ('ltr' == 'ltr' && 'center' == 'end') ||
   1321             ('ltr' == 'rtl' && 'center' == 'start')) {
   1322      offset -= width;
   1323    }
   1324    return offset;
   1325  }
   1326 
   1327 
   1328  function placeAndMeasureTextInDOM(text, text_width, point) {
   1329    const el = document.createElement("p");
   1330    el.innerHTML = text;
   1331    el.style.font = '50px sans-serif';
   1332    el.style.direction = 'ltr';
   1333    el.style.letterSpacing = '10px';
   1334    // Put the text top left to make offsets simpler.
   1335    el.style.padding = '0px';
   1336    el.style.margin = '0px';
   1337    el.style.position = 'absolute';
   1338    el.style.x = '0px';
   1339    el.style.y = '0px';
   1340    document.body.appendChild(el);
   1341    text_bound = el.getBoundingClientRect();
   1342    text_x = text_bound.x;
   1343    text_y = text_bound.y + text_bound.height / 2;
   1344 
   1345    // Offset to the requested point determined by textAlign and direction.
   1346    let text_align_dx = 0;
   1347    if ('center' == 'center') {
   1348      text_align_dx = text_width / 2;
   1349    } else if ('center' == 'right' ||
   1350             ('ltr' == 'ltr' && 'center' == 'end') ||
   1351             ('ltr' == 'rtl' && 'center' == 'start')) {
   1352      text_align_dx = text_width;
   1353    }
   1354    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1355    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1356 
   1357    document.body.removeChild(el);
   1358 
   1359    return position.offset;
   1360  }
   1361 
   1362  ctx.font = '50px sans-serif';
   1363  ctx.direction = 'ltr';
   1364  ctx.textAlign = 'center';
   1365  ctx.letterSpacing = '10px';
   1366 
   1367  const kTexts = [
   1368    'UNAVAILABLE',
   1369    '🏁🎢🏁',
   1370    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1371    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1372    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1373    '--abcd__'
   1374  ]
   1375 
   1376  for (text of kTexts) {
   1377    const tm = ctx.measureText(text);
   1378    text_width = tm.width;
   1379    step = 30;
   1380    if ('10px' == '10px') {
   1381      step = 40;
   1382    }
   1383 
   1384    offset = step;
   1385    adjusted_offset = alignOffset(offset, text_width);
   1386    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1387    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1388    assert_equals(tm_position,
   1389                  doc_position,
   1390                  "for " + text + " offset " + offset);
   1391 
   1392    offset = text_width / 2 - 10;
   1393    adjusted_offset = alignOffset(offset, text_width);
   1394    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1395    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1396    assert_equals(tm_position,
   1397                  doc_position,
   1398                  "for " + text + " offset " + offset);
   1399 
   1400    offset = text_width / 2 + 10;
   1401    adjusted_offset = alignOffset(offset, text_width);
   1402    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1403    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1404    assert_equals(tm_position,
   1405                  doc_position,
   1406                  "for " + text + " offset " + offset);
   1407 
   1408    offset = text_width - step;
   1409    adjusted_offset = alignOffset(offset, text_width);
   1410    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1411    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1412    assert_equals(tm_position,
   1413                  doc_position,
   1414                  "for " + text + " offset " + offset);
   1415  }
   1416 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and no-directional-override.");
   1417 
   1418 test(t => {
   1419  const canvas = document.createElement('canvas');
   1420  canvas.width = 100;
   1421  canvas.height = 50;
   1422  const ctx = canvas.getContext('2d');
   1423 
   1424  function alignOffset(offset, width) {
   1425    if ('center' == 'center') {
   1426      offset -= width / 2;
   1427    } else if ('center' == 'right' ||
   1428             ('rtl' == 'ltr' && 'center' == 'end') ||
   1429             ('rtl' == 'rtl' && 'center' == 'start')) {
   1430      offset -= width;
   1431    }
   1432    return offset;
   1433  }
   1434 
   1435 
   1436  function placeAndMeasureTextInDOM(text, text_width, point) {
   1437    const el = document.createElement("p");
   1438    el.innerHTML = text;
   1439    el.style.font = '50px sans-serif';
   1440    el.style.direction = 'rtl';
   1441    el.style.letterSpacing = '10px';
   1442    // Put the text top left to make offsets simpler.
   1443    el.style.padding = '0px';
   1444    el.style.margin = '0px';
   1445    el.style.position = 'absolute';
   1446    el.style.x = '0px';
   1447    el.style.y = '0px';
   1448    document.body.appendChild(el);
   1449    text_bound = el.getBoundingClientRect();
   1450    text_x = text_bound.x;
   1451    text_y = text_bound.y + text_bound.height / 2;
   1452 
   1453    // Offset to the requested point determined by textAlign and direction.
   1454    let text_align_dx = 0;
   1455    if ('center' == 'center') {
   1456      text_align_dx = text_width / 2;
   1457    } else if ('center' == 'right' ||
   1458             ('rtl' == 'ltr' && 'center' == 'end') ||
   1459             ('rtl' == 'rtl' && 'center' == 'start')) {
   1460      text_align_dx = text_width;
   1461    }
   1462    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1463    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1464 
   1465    document.body.removeChild(el);
   1466 
   1467    return position.offset;
   1468  }
   1469 
   1470  ctx.font = '50px sans-serif';
   1471  ctx.direction = 'rtl';
   1472  ctx.textAlign = 'center';
   1473  ctx.letterSpacing = '10px';
   1474 
   1475  const kTexts = [
   1476    'UNAVAILABLE',
   1477    '🏁🎢🏁',
   1478    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1479    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1480    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1481    '--abcd__'
   1482  ]
   1483 
   1484  for (text of kTexts) {
   1485    const tm = ctx.measureText(text);
   1486    text_width = tm.width;
   1487    step = 30;
   1488    if ('10px' == '10px') {
   1489      step = 40;
   1490    }
   1491 
   1492    offset = step;
   1493    adjusted_offset = alignOffset(offset, text_width);
   1494    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1495    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1496    assert_equals(tm_position,
   1497                  doc_position,
   1498                  "for " + text + " offset " + offset);
   1499 
   1500    offset = text_width / 2 - 10;
   1501    adjusted_offset = alignOffset(offset, text_width);
   1502    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1503    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1504    assert_equals(tm_position,
   1505                  doc_position,
   1506                  "for " + text + " offset " + offset);
   1507 
   1508    offset = text_width / 2 + 10;
   1509    adjusted_offset = alignOffset(offset, text_width);
   1510    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1511    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1512    assert_equals(tm_position,
   1513                  doc_position,
   1514                  "for " + text + " offset " + offset);
   1515 
   1516    offset = text_width - step;
   1517    adjusted_offset = alignOffset(offset, text_width);
   1518    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1519    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1520    assert_equals(tm_position,
   1521                  doc_position,
   1522                  "for " + text + " offset " + offset);
   1523  }
   1524 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and no-directional-override.");
   1525 
   1526 test(t => {
   1527  const canvas = document.createElement('canvas');
   1528  canvas.width = 100;
   1529  canvas.height = 50;
   1530  const ctx = canvas.getContext('2d');
   1531 
   1532  function alignOffset(offset, width) {
   1533    if ('right' == 'center') {
   1534      offset -= width / 2;
   1535    } else if ('right' == 'right' ||
   1536             ('ltr' == 'ltr' && 'right' == 'end') ||
   1537             ('ltr' == 'rtl' && 'right' == 'start')) {
   1538      offset -= width;
   1539    }
   1540    return offset;
   1541  }
   1542 
   1543 
   1544  function placeAndMeasureTextInDOM(text, text_width, point) {
   1545    const el = document.createElement("p");
   1546    el.innerHTML = text;
   1547    el.style.font = '50px sans-serif';
   1548    el.style.direction = 'ltr';
   1549    el.style.letterSpacing = '10px';
   1550    // Put the text top left to make offsets simpler.
   1551    el.style.padding = '0px';
   1552    el.style.margin = '0px';
   1553    el.style.position = 'absolute';
   1554    el.style.x = '0px';
   1555    el.style.y = '0px';
   1556    document.body.appendChild(el);
   1557    text_bound = el.getBoundingClientRect();
   1558    text_x = text_bound.x;
   1559    text_y = text_bound.y + text_bound.height / 2;
   1560 
   1561    // Offset to the requested point determined by textAlign and direction.
   1562    let text_align_dx = 0;
   1563    if ('right' == 'center') {
   1564      text_align_dx = text_width / 2;
   1565    } else if ('right' == 'right' ||
   1566             ('ltr' == 'ltr' && 'right' == 'end') ||
   1567             ('ltr' == 'rtl' && 'right' == 'start')) {
   1568      text_align_dx = text_width;
   1569    }
   1570    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1571    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1572 
   1573    document.body.removeChild(el);
   1574 
   1575    return position.offset;
   1576  }
   1577 
   1578  ctx.font = '50px sans-serif';
   1579  ctx.direction = 'ltr';
   1580  ctx.textAlign = 'right';
   1581  ctx.letterSpacing = '10px';
   1582 
   1583  const kTexts = [
   1584    'UNAVAILABLE',
   1585    '🏁🎢🏁',
   1586    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1587    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1588    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1589    '--abcd__'
   1590  ]
   1591 
   1592  for (text of kTexts) {
   1593    const tm = ctx.measureText(text);
   1594    text_width = tm.width;
   1595    step = 30;
   1596    if ('10px' == '10px') {
   1597      step = 40;
   1598    }
   1599 
   1600    offset = step;
   1601    adjusted_offset = alignOffset(offset, text_width);
   1602    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1603    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1604    assert_equals(tm_position,
   1605                  doc_position,
   1606                  "for " + text + " offset " + offset);
   1607 
   1608    offset = text_width / 2 - 10;
   1609    adjusted_offset = alignOffset(offset, text_width);
   1610    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1611    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1612    assert_equals(tm_position,
   1613                  doc_position,
   1614                  "for " + text + " offset " + offset);
   1615 
   1616    offset = text_width / 2 + 10;
   1617    adjusted_offset = alignOffset(offset, text_width);
   1618    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1619    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1620    assert_equals(tm_position,
   1621                  doc_position,
   1622                  "for " + text + " offset " + offset);
   1623 
   1624    offset = text_width - step;
   1625    adjusted_offset = alignOffset(offset, text_width);
   1626    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1627    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1628    assert_equals(tm_position,
   1629                  doc_position,
   1630                  "for " + text + " offset " + offset);
   1631  }
   1632 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and no-directional-override.");
   1633 
   1634 test(t => {
   1635  const canvas = document.createElement('canvas');
   1636  canvas.width = 100;
   1637  canvas.height = 50;
   1638  const ctx = canvas.getContext('2d');
   1639 
   1640  function alignOffset(offset, width) {
   1641    if ('right' == 'center') {
   1642      offset -= width / 2;
   1643    } else if ('right' == 'right' ||
   1644             ('rtl' == 'ltr' && 'right' == 'end') ||
   1645             ('rtl' == 'rtl' && 'right' == 'start')) {
   1646      offset -= width;
   1647    }
   1648    return offset;
   1649  }
   1650 
   1651 
   1652  function placeAndMeasureTextInDOM(text, text_width, point) {
   1653    const el = document.createElement("p");
   1654    el.innerHTML = text;
   1655    el.style.font = '50px sans-serif';
   1656    el.style.direction = 'rtl';
   1657    el.style.letterSpacing = '10px';
   1658    // Put the text top left to make offsets simpler.
   1659    el.style.padding = '0px';
   1660    el.style.margin = '0px';
   1661    el.style.position = 'absolute';
   1662    el.style.x = '0px';
   1663    el.style.y = '0px';
   1664    document.body.appendChild(el);
   1665    text_bound = el.getBoundingClientRect();
   1666    text_x = text_bound.x;
   1667    text_y = text_bound.y + text_bound.height / 2;
   1668 
   1669    // Offset to the requested point determined by textAlign and direction.
   1670    let text_align_dx = 0;
   1671    if ('right' == 'center') {
   1672      text_align_dx = text_width / 2;
   1673    } else if ('right' == 'right' ||
   1674             ('rtl' == 'ltr' && 'right' == 'end') ||
   1675             ('rtl' == 'rtl' && 'right' == 'start')) {
   1676      text_align_dx = text_width;
   1677    }
   1678    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1679    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1680 
   1681    document.body.removeChild(el);
   1682 
   1683    return position.offset;
   1684  }
   1685 
   1686  ctx.font = '50px sans-serif';
   1687  ctx.direction = 'rtl';
   1688  ctx.textAlign = 'right';
   1689  ctx.letterSpacing = '10px';
   1690 
   1691  const kTexts = [
   1692    'UNAVAILABLE',
   1693    '🏁🎢🏁',
   1694    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1695    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1696    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1697    '--abcd__'
   1698  ]
   1699 
   1700  for (text of kTexts) {
   1701    const tm = ctx.measureText(text);
   1702    text_width = tm.width;
   1703    step = 30;
   1704    if ('10px' == '10px') {
   1705      step = 40;
   1706    }
   1707 
   1708    offset = step;
   1709    adjusted_offset = alignOffset(offset, text_width);
   1710    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1711    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1712    assert_equals(tm_position,
   1713                  doc_position,
   1714                  "for " + text + " offset " + offset);
   1715 
   1716    offset = text_width / 2 - 10;
   1717    adjusted_offset = alignOffset(offset, text_width);
   1718    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1719    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1720    assert_equals(tm_position,
   1721                  doc_position,
   1722                  "for " + text + " offset " + offset);
   1723 
   1724    offset = text_width / 2 + 10;
   1725    adjusted_offset = alignOffset(offset, text_width);
   1726    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1727    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1728    assert_equals(tm_position,
   1729                  doc_position,
   1730                  "for " + text + " offset " + offset);
   1731 
   1732    offset = text_width - step;
   1733    adjusted_offset = alignOffset(offset, text_width);
   1734    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1735    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1736    assert_equals(tm_position,
   1737                  doc_position,
   1738                  "for " + text + " offset " + offset);
   1739  }
   1740 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and no-directional-override.");
   1741 
   1742 test(t => {
   1743  const canvas = document.createElement('canvas');
   1744  canvas.width = 100;
   1745  canvas.height = 50;
   1746  const ctx = canvas.getContext('2d');
   1747 
   1748  function alignOffset(offset, width) {
   1749    if ('start' == 'center') {
   1750      offset -= width / 2;
   1751    } else if ('start' == 'right' ||
   1752             ('ltr' == 'ltr' && 'start' == 'end') ||
   1753             ('ltr' == 'rtl' && 'start' == 'start')) {
   1754      offset -= width;
   1755    }
   1756    return offset;
   1757  }
   1758 
   1759 
   1760  function placeAndMeasureTextInDOM(text, text_width, point) {
   1761    const el = document.createElement("p");
   1762    el.innerHTML = text;
   1763    el.style.font = '50px sans-serif';
   1764    el.style.direction = 'ltr';
   1765    el.style.letterSpacing = '10px';
   1766    // Put the text top left to make offsets simpler.
   1767    el.style.padding = '0px';
   1768    el.style.margin = '0px';
   1769    el.style.position = 'absolute';
   1770    el.style.x = '0px';
   1771    el.style.y = '0px';
   1772    document.body.appendChild(el);
   1773    text_bound = el.getBoundingClientRect();
   1774    text_x = text_bound.x;
   1775    text_y = text_bound.y + text_bound.height / 2;
   1776 
   1777    // Offset to the requested point determined by textAlign and direction.
   1778    let text_align_dx = 0;
   1779    if ('start' == 'center') {
   1780      text_align_dx = text_width / 2;
   1781    } else if ('start' == 'right' ||
   1782             ('ltr' == 'ltr' && 'start' == 'end') ||
   1783             ('ltr' == 'rtl' && 'start' == 'start')) {
   1784      text_align_dx = text_width;
   1785    }
   1786    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1787    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1788 
   1789    document.body.removeChild(el);
   1790 
   1791    return position.offset;
   1792  }
   1793 
   1794  ctx.font = '50px sans-serif';
   1795  ctx.direction = 'ltr';
   1796  ctx.textAlign = 'start';
   1797  ctx.letterSpacing = '10px';
   1798 
   1799  const kTexts = [
   1800    'UNAVAILABLE',
   1801    '🏁🎢🏁',
   1802    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1803    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1804    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1805    '--abcd__'
   1806  ]
   1807 
   1808  for (text of kTexts) {
   1809    const tm = ctx.measureText(text);
   1810    text_width = tm.width;
   1811    step = 30;
   1812    if ('10px' == '10px') {
   1813      step = 40;
   1814    }
   1815 
   1816    offset = step;
   1817    adjusted_offset = alignOffset(offset, text_width);
   1818    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1819    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1820    assert_equals(tm_position,
   1821                  doc_position,
   1822                  "for " + text + " offset " + offset);
   1823 
   1824    offset = text_width / 2 - 10;
   1825    adjusted_offset = alignOffset(offset, text_width);
   1826    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1827    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1828    assert_equals(tm_position,
   1829                  doc_position,
   1830                  "for " + text + " offset " + offset);
   1831 
   1832    offset = text_width / 2 + 10;
   1833    adjusted_offset = alignOffset(offset, text_width);
   1834    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1835    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1836    assert_equals(tm_position,
   1837                  doc_position,
   1838                  "for " + text + " offset " + offset);
   1839 
   1840    offset = text_width - step;
   1841    adjusted_offset = alignOffset(offset, text_width);
   1842    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1843    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1844    assert_equals(tm_position,
   1845                  doc_position,
   1846                  "for " + text + " offset " + offset);
   1847  }
   1848 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and no-directional-override.");
   1849 
   1850 test(t => {
   1851  const canvas = document.createElement('canvas');
   1852  canvas.width = 100;
   1853  canvas.height = 50;
   1854  const ctx = canvas.getContext('2d');
   1855 
   1856  function alignOffset(offset, width) {
   1857    if ('start' == 'center') {
   1858      offset -= width / 2;
   1859    } else if ('start' == 'right' ||
   1860             ('rtl' == 'ltr' && 'start' == 'end') ||
   1861             ('rtl' == 'rtl' && 'start' == 'start')) {
   1862      offset -= width;
   1863    }
   1864    return offset;
   1865  }
   1866 
   1867 
   1868  function placeAndMeasureTextInDOM(text, text_width, point) {
   1869    const el = document.createElement("p");
   1870    el.innerHTML = text;
   1871    el.style.font = '50px sans-serif';
   1872    el.style.direction = 'rtl';
   1873    el.style.letterSpacing = '10px';
   1874    // Put the text top left to make offsets simpler.
   1875    el.style.padding = '0px';
   1876    el.style.margin = '0px';
   1877    el.style.position = 'absolute';
   1878    el.style.x = '0px';
   1879    el.style.y = '0px';
   1880    document.body.appendChild(el);
   1881    text_bound = el.getBoundingClientRect();
   1882    text_x = text_bound.x;
   1883    text_y = text_bound.y + text_bound.height / 2;
   1884 
   1885    // Offset to the requested point determined by textAlign and direction.
   1886    let text_align_dx = 0;
   1887    if ('start' == 'center') {
   1888      text_align_dx = text_width / 2;
   1889    } else if ('start' == 'right' ||
   1890             ('rtl' == 'ltr' && 'start' == 'end') ||
   1891             ('rtl' == 'rtl' && 'start' == 'start')) {
   1892      text_align_dx = text_width;
   1893    }
   1894    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   1895    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   1896 
   1897    document.body.removeChild(el);
   1898 
   1899    return position.offset;
   1900  }
   1901 
   1902  ctx.font = '50px sans-serif';
   1903  ctx.direction = 'rtl';
   1904  ctx.textAlign = 'start';
   1905  ctx.letterSpacing = '10px';
   1906 
   1907  const kTexts = [
   1908    'UNAVAILABLE',
   1909    '🏁🎢🏁',
   1910    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   1911    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   1912    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   1913    '--abcd__'
   1914  ]
   1915 
   1916  for (text of kTexts) {
   1917    const tm = ctx.measureText(text);
   1918    text_width = tm.width;
   1919    step = 30;
   1920    if ('10px' == '10px') {
   1921      step = 40;
   1922    }
   1923 
   1924    offset = step;
   1925    adjusted_offset = alignOffset(offset, text_width);
   1926    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1927    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1928    assert_equals(tm_position,
   1929                  doc_position,
   1930                  "for " + text + " offset " + offset);
   1931 
   1932    offset = text_width / 2 - 10;
   1933    adjusted_offset = alignOffset(offset, text_width);
   1934    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1935    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1936    assert_equals(tm_position,
   1937                  doc_position,
   1938                  "for " + text + " offset " + offset);
   1939 
   1940    offset = text_width / 2 + 10;
   1941    adjusted_offset = alignOffset(offset, text_width);
   1942    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1943    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1944    assert_equals(tm_position,
   1945                  doc_position,
   1946                  "for " + text + " offset " + offset);
   1947 
   1948    offset = text_width - step;
   1949    adjusted_offset = alignOffset(offset, text_width);
   1950    tm_position = tm.getIndexFromOffset(adjusted_offset);
   1951    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   1952    assert_equals(tm_position,
   1953                  doc_position,
   1954                  "for " + text + " offset " + offset);
   1955  }
   1956 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and no-directional-override.");
   1957 
   1958 test(t => {
   1959  const canvas = document.createElement('canvas');
   1960  canvas.width = 100;
   1961  canvas.height = 50;
   1962  const ctx = canvas.getContext('2d');
   1963 
   1964  function alignOffset(offset, width) {
   1965    if ('end' == 'center') {
   1966      offset -= width / 2;
   1967    } else if ('end' == 'right' ||
   1968             ('ltr' == 'ltr' && 'end' == 'end') ||
   1969             ('ltr' == 'rtl' && 'end' == 'start')) {
   1970      offset -= width;
   1971    }
   1972    return offset;
   1973  }
   1974 
   1975 
   1976  function placeAndMeasureTextInDOM(text, text_width, point) {
   1977    const el = document.createElement("p");
   1978    el.innerHTML = text;
   1979    el.style.font = '50px sans-serif';
   1980    el.style.direction = 'ltr';
   1981    el.style.letterSpacing = '10px';
   1982    // Put the text top left to make offsets simpler.
   1983    el.style.padding = '0px';
   1984    el.style.margin = '0px';
   1985    el.style.position = 'absolute';
   1986    el.style.x = '0px';
   1987    el.style.y = '0px';
   1988    document.body.appendChild(el);
   1989    text_bound = el.getBoundingClientRect();
   1990    text_x = text_bound.x;
   1991    text_y = text_bound.y + text_bound.height / 2;
   1992 
   1993    // Offset to the requested point determined by textAlign and direction.
   1994    let text_align_dx = 0;
   1995    if ('end' == 'center') {
   1996      text_align_dx = text_width / 2;
   1997    } else if ('end' == 'right' ||
   1998             ('ltr' == 'ltr' && 'end' == 'end') ||
   1999             ('ltr' == 'rtl' && 'end' == 'start')) {
   2000      text_align_dx = text_width;
   2001    }
   2002    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2003    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2004 
   2005    document.body.removeChild(el);
   2006 
   2007    return position.offset;
   2008  }
   2009 
   2010  ctx.font = '50px sans-serif';
   2011  ctx.direction = 'ltr';
   2012  ctx.textAlign = 'end';
   2013  ctx.letterSpacing = '10px';
   2014 
   2015  const kTexts = [
   2016    'UNAVAILABLE',
   2017    '🏁🎢🏁',
   2018    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2019    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2020    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2021    '--abcd__'
   2022  ]
   2023 
   2024  for (text of kTexts) {
   2025    const tm = ctx.measureText(text);
   2026    text_width = tm.width;
   2027    step = 30;
   2028    if ('10px' == '10px') {
   2029      step = 40;
   2030    }
   2031 
   2032    offset = step;
   2033    adjusted_offset = alignOffset(offset, text_width);
   2034    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2035    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2036    assert_equals(tm_position,
   2037                  doc_position,
   2038                  "for " + text + " offset " + offset);
   2039 
   2040    offset = text_width / 2 - 10;
   2041    adjusted_offset = alignOffset(offset, text_width);
   2042    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2043    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2044    assert_equals(tm_position,
   2045                  doc_position,
   2046                  "for " + text + " offset " + offset);
   2047 
   2048    offset = text_width / 2 + 10;
   2049    adjusted_offset = alignOffset(offset, text_width);
   2050    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2051    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2052    assert_equals(tm_position,
   2053                  doc_position,
   2054                  "for " + text + " offset " + offset);
   2055 
   2056    offset = text_width - step;
   2057    adjusted_offset = alignOffset(offset, text_width);
   2058    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2059    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2060    assert_equals(tm_position,
   2061                  doc_position,
   2062                  "for " + text + " offset " + offset);
   2063  }
   2064 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and no-directional-override.");
   2065 
   2066 test(t => {
   2067  const canvas = document.createElement('canvas');
   2068  canvas.width = 100;
   2069  canvas.height = 50;
   2070  const ctx = canvas.getContext('2d');
   2071 
   2072  function alignOffset(offset, width) {
   2073    if ('end' == 'center') {
   2074      offset -= width / 2;
   2075    } else if ('end' == 'right' ||
   2076             ('rtl' == 'ltr' && 'end' == 'end') ||
   2077             ('rtl' == 'rtl' && 'end' == 'start')) {
   2078      offset -= width;
   2079    }
   2080    return offset;
   2081  }
   2082 
   2083 
   2084  function placeAndMeasureTextInDOM(text, text_width, point) {
   2085    const el = document.createElement("p");
   2086    el.innerHTML = text;
   2087    el.style.font = '50px sans-serif';
   2088    el.style.direction = 'rtl';
   2089    el.style.letterSpacing = '10px';
   2090    // Put the text top left to make offsets simpler.
   2091    el.style.padding = '0px';
   2092    el.style.margin = '0px';
   2093    el.style.position = 'absolute';
   2094    el.style.x = '0px';
   2095    el.style.y = '0px';
   2096    document.body.appendChild(el);
   2097    text_bound = el.getBoundingClientRect();
   2098    text_x = text_bound.x;
   2099    text_y = text_bound.y + text_bound.height / 2;
   2100 
   2101    // Offset to the requested point determined by textAlign and direction.
   2102    let text_align_dx = 0;
   2103    if ('end' == 'center') {
   2104      text_align_dx = text_width / 2;
   2105    } else if ('end' == 'right' ||
   2106             ('rtl' == 'ltr' && 'end' == 'end') ||
   2107             ('rtl' == 'rtl' && 'end' == 'start')) {
   2108      text_align_dx = text_width;
   2109    }
   2110    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2111    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2112 
   2113    document.body.removeChild(el);
   2114 
   2115    return position.offset;
   2116  }
   2117 
   2118  ctx.font = '50px sans-serif';
   2119  ctx.direction = 'rtl';
   2120  ctx.textAlign = 'end';
   2121  ctx.letterSpacing = '10px';
   2122 
   2123  const kTexts = [
   2124    'UNAVAILABLE',
   2125    '🏁🎢🏁',
   2126    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2127    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2128    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2129    '--abcd__'
   2130  ]
   2131 
   2132  for (text of kTexts) {
   2133    const tm = ctx.measureText(text);
   2134    text_width = tm.width;
   2135    step = 30;
   2136    if ('10px' == '10px') {
   2137      step = 40;
   2138    }
   2139 
   2140    offset = step;
   2141    adjusted_offset = alignOffset(offset, text_width);
   2142    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2143    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2144    assert_equals(tm_position,
   2145                  doc_position,
   2146                  "for " + text + " offset " + offset);
   2147 
   2148    offset = text_width / 2 - 10;
   2149    adjusted_offset = alignOffset(offset, text_width);
   2150    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2151    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2152    assert_equals(tm_position,
   2153                  doc_position,
   2154                  "for " + text + " offset " + offset);
   2155 
   2156    offset = text_width / 2 + 10;
   2157    adjusted_offset = alignOffset(offset, text_width);
   2158    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2159    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2160    assert_equals(tm_position,
   2161                  doc_position,
   2162                  "for " + text + " offset " + offset);
   2163 
   2164    offset = text_width - step;
   2165    adjusted_offset = alignOffset(offset, text_width);
   2166    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2167    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2168    assert_equals(tm_position,
   2169                  doc_position,
   2170                  "for " + text + " offset " + offset);
   2171  }
   2172 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and no-directional-override.");
   2173 
   2174 test(t => {
   2175  const canvas = document.createElement('canvas');
   2176  canvas.width = 100;
   2177  canvas.height = 50;
   2178  const ctx = canvas.getContext('2d');
   2179 
   2180  function alignOffset(offset, width) {
   2181    if ('left' == 'center') {
   2182      offset -= width / 2;
   2183    } else if ('left' == 'right' ||
   2184             ('ltr' == 'ltr' && 'left' == 'end') ||
   2185             ('ltr' == 'rtl' && 'left' == 'start')) {
   2186      offset -= width;
   2187    }
   2188    return offset;
   2189  }
   2190 
   2191  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2192    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2193    const LTR_OVERRIDE = '\u202D';
   2194    const RTL_OVERRIDE = '\u202E';
   2195    const OVERRIDE_END = '\u202C';
   2196    if (direction_is_ltr) {
   2197      return LTR_OVERRIDE + text + OVERRIDE_END;
   2198    }
   2199    return RTL_OVERRIDE + text + OVERRIDE_END;
   2200  }
   2201 
   2202  function placeAndMeasureTextInDOM(text, text_width, point) {
   2203    const el = document.createElement("p");
   2204    el.innerHTML = text;
   2205    el.style.font = '50px sans-serif';
   2206    el.style.direction = 'ltr';
   2207    el.style.letterSpacing = '0px';
   2208    // Put the text top left to make offsets simpler.
   2209    el.style.padding = '0px';
   2210    el.style.margin = '0px';
   2211    el.style.position = 'absolute';
   2212    el.style.x = '0px';
   2213    el.style.y = '0px';
   2214    document.body.appendChild(el);
   2215    text_bound = el.getBoundingClientRect();
   2216    text_x = text_bound.x;
   2217    text_y = text_bound.y + text_bound.height / 2;
   2218 
   2219    // Offset to the requested point determined by textAlign and direction.
   2220    let text_align_dx = 0;
   2221    if ('left' == 'center') {
   2222      text_align_dx = text_width / 2;
   2223    } else if ('left' == 'right' ||
   2224             ('ltr' == 'ltr' && 'left' == 'end') ||
   2225             ('ltr' == 'rtl' && 'left' == 'start')) {
   2226      text_align_dx = text_width;
   2227    }
   2228    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2229    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2230 
   2231    document.body.removeChild(el);
   2232 
   2233    return position.offset;
   2234  }
   2235 
   2236  ctx.font = '50px sans-serif';
   2237  ctx.direction = 'ltr';
   2238  ctx.textAlign = 'left';
   2239  ctx.letterSpacing = '0px';
   2240 
   2241  const kTexts = [
   2242    'UNAVAILABLE',
   2243    '🏁🎢🏁',
   2244    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2245    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2246    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2247    '--abcd__'
   2248  ]
   2249 
   2250  for (text of kTexts) {
   2251    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2252    const tm = ctx.measureText(text);
   2253    text_width = tm.width;
   2254    step = 30;
   2255    if ('0px' == '10px') {
   2256      step = 40;
   2257    }
   2258 
   2259    offset = step;
   2260    adjusted_offset = alignOffset(offset, text_width);
   2261    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2262    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2263    assert_equals(tm_position,
   2264                  doc_position,
   2265                  "for " + text + " offset " + offset);
   2266 
   2267    offset = text_width / 2 - 10;
   2268    adjusted_offset = alignOffset(offset, text_width);
   2269    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2270    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2271    assert_equals(tm_position,
   2272                  doc_position,
   2273                  "for " + text + " offset " + offset);
   2274 
   2275    offset = text_width / 2 + 10;
   2276    adjusted_offset = alignOffset(offset, text_width);
   2277    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2278    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2279    assert_equals(tm_position,
   2280                  doc_position,
   2281                  "for " + text + " offset " + offset);
   2282 
   2283    offset = text_width - step;
   2284    adjusted_offset = alignOffset(offset, text_width);
   2285    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2286    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2287    assert_equals(tm_position,
   2288                  doc_position,
   2289                  "for " + text + " offset " + offset);
   2290  }
   2291 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and directional-override.");
   2292 
   2293 test(t => {
   2294  const canvas = document.createElement('canvas');
   2295  canvas.width = 100;
   2296  canvas.height = 50;
   2297  const ctx = canvas.getContext('2d');
   2298 
   2299  function alignOffset(offset, width) {
   2300    if ('left' == 'center') {
   2301      offset -= width / 2;
   2302    } else if ('left' == 'right' ||
   2303             ('rtl' == 'ltr' && 'left' == 'end') ||
   2304             ('rtl' == 'rtl' && 'left' == 'start')) {
   2305      offset -= width;
   2306    }
   2307    return offset;
   2308  }
   2309 
   2310  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2311    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2312    const LTR_OVERRIDE = '\u202D';
   2313    const RTL_OVERRIDE = '\u202E';
   2314    const OVERRIDE_END = '\u202C';
   2315    if (direction_is_ltr) {
   2316      return LTR_OVERRIDE + text + OVERRIDE_END;
   2317    }
   2318    return RTL_OVERRIDE + text + OVERRIDE_END;
   2319  }
   2320 
   2321  function placeAndMeasureTextInDOM(text, text_width, point) {
   2322    const el = document.createElement("p");
   2323    el.innerHTML = text;
   2324    el.style.font = '50px sans-serif';
   2325    el.style.direction = 'rtl';
   2326    el.style.letterSpacing = '0px';
   2327    // Put the text top left to make offsets simpler.
   2328    el.style.padding = '0px';
   2329    el.style.margin = '0px';
   2330    el.style.position = 'absolute';
   2331    el.style.x = '0px';
   2332    el.style.y = '0px';
   2333    document.body.appendChild(el);
   2334    text_bound = el.getBoundingClientRect();
   2335    text_x = text_bound.x;
   2336    text_y = text_bound.y + text_bound.height / 2;
   2337 
   2338    // Offset to the requested point determined by textAlign and direction.
   2339    let text_align_dx = 0;
   2340    if ('left' == 'center') {
   2341      text_align_dx = text_width / 2;
   2342    } else if ('left' == 'right' ||
   2343             ('rtl' == 'ltr' && 'left' == 'end') ||
   2344             ('rtl' == 'rtl' && 'left' == 'start')) {
   2345      text_align_dx = text_width;
   2346    }
   2347    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2348    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2349 
   2350    document.body.removeChild(el);
   2351 
   2352    return position.offset;
   2353  }
   2354 
   2355  ctx.font = '50px sans-serif';
   2356  ctx.direction = 'rtl';
   2357  ctx.textAlign = 'left';
   2358  ctx.letterSpacing = '0px';
   2359 
   2360  const kTexts = [
   2361    'UNAVAILABLE',
   2362    '🏁🎢🏁',
   2363    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2364    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2365    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2366    '--abcd__'
   2367  ]
   2368 
   2369  for (text of kTexts) {
   2370    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2371    const tm = ctx.measureText(text);
   2372    text_width = tm.width;
   2373    step = 30;
   2374    if ('0px' == '10px') {
   2375      step = 40;
   2376    }
   2377 
   2378    offset = step;
   2379    adjusted_offset = alignOffset(offset, text_width);
   2380    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2381    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2382    assert_equals(tm_position,
   2383                  doc_position,
   2384                  "for " + text + " offset " + offset);
   2385 
   2386    offset = text_width / 2 - 10;
   2387    adjusted_offset = alignOffset(offset, text_width);
   2388    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2389    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2390    assert_equals(tm_position,
   2391                  doc_position,
   2392                  "for " + text + " offset " + offset);
   2393 
   2394    offset = text_width / 2 + 10;
   2395    adjusted_offset = alignOffset(offset, text_width);
   2396    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2397    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2398    assert_equals(tm_position,
   2399                  doc_position,
   2400                  "for " + text + " offset " + offset);
   2401 
   2402    offset = text_width - step;
   2403    adjusted_offset = alignOffset(offset, text_width);
   2404    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2405    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2406    assert_equals(tm_position,
   2407                  doc_position,
   2408                  "for " + text + " offset " + offset);
   2409  }
   2410 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and directional-override.");
   2411 
   2412 test(t => {
   2413  const canvas = document.createElement('canvas');
   2414  canvas.width = 100;
   2415  canvas.height = 50;
   2416  const ctx = canvas.getContext('2d');
   2417 
   2418  function alignOffset(offset, width) {
   2419    if ('center' == 'center') {
   2420      offset -= width / 2;
   2421    } else if ('center' == 'right' ||
   2422             ('ltr' == 'ltr' && 'center' == 'end') ||
   2423             ('ltr' == 'rtl' && 'center' == 'start')) {
   2424      offset -= width;
   2425    }
   2426    return offset;
   2427  }
   2428 
   2429  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2430    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2431    const LTR_OVERRIDE = '\u202D';
   2432    const RTL_OVERRIDE = '\u202E';
   2433    const OVERRIDE_END = '\u202C';
   2434    if (direction_is_ltr) {
   2435      return LTR_OVERRIDE + text + OVERRIDE_END;
   2436    }
   2437    return RTL_OVERRIDE + text + OVERRIDE_END;
   2438  }
   2439 
   2440  function placeAndMeasureTextInDOM(text, text_width, point) {
   2441    const el = document.createElement("p");
   2442    el.innerHTML = text;
   2443    el.style.font = '50px sans-serif';
   2444    el.style.direction = 'ltr';
   2445    el.style.letterSpacing = '0px';
   2446    // Put the text top left to make offsets simpler.
   2447    el.style.padding = '0px';
   2448    el.style.margin = '0px';
   2449    el.style.position = 'absolute';
   2450    el.style.x = '0px';
   2451    el.style.y = '0px';
   2452    document.body.appendChild(el);
   2453    text_bound = el.getBoundingClientRect();
   2454    text_x = text_bound.x;
   2455    text_y = text_bound.y + text_bound.height / 2;
   2456 
   2457    // Offset to the requested point determined by textAlign and direction.
   2458    let text_align_dx = 0;
   2459    if ('center' == 'center') {
   2460      text_align_dx = text_width / 2;
   2461    } else if ('center' == 'right' ||
   2462             ('ltr' == 'ltr' && 'center' == 'end') ||
   2463             ('ltr' == 'rtl' && 'center' == 'start')) {
   2464      text_align_dx = text_width;
   2465    }
   2466    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2467    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2468 
   2469    document.body.removeChild(el);
   2470 
   2471    return position.offset;
   2472  }
   2473 
   2474  ctx.font = '50px sans-serif';
   2475  ctx.direction = 'ltr';
   2476  ctx.textAlign = 'center';
   2477  ctx.letterSpacing = '0px';
   2478 
   2479  const kTexts = [
   2480    'UNAVAILABLE',
   2481    '🏁🎢🏁',
   2482    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2483    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2484    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2485    '--abcd__'
   2486  ]
   2487 
   2488  for (text of kTexts) {
   2489    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2490    const tm = ctx.measureText(text);
   2491    text_width = tm.width;
   2492    step = 30;
   2493    if ('0px' == '10px') {
   2494      step = 40;
   2495    }
   2496 
   2497    offset = step;
   2498    adjusted_offset = alignOffset(offset, text_width);
   2499    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2500    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2501    assert_equals(tm_position,
   2502                  doc_position,
   2503                  "for " + text + " offset " + offset);
   2504 
   2505    offset = text_width / 2 - 10;
   2506    adjusted_offset = alignOffset(offset, text_width);
   2507    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2508    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2509    assert_equals(tm_position,
   2510                  doc_position,
   2511                  "for " + text + " offset " + offset);
   2512 
   2513    offset = text_width / 2 + 10;
   2514    adjusted_offset = alignOffset(offset, text_width);
   2515    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2516    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2517    assert_equals(tm_position,
   2518                  doc_position,
   2519                  "for " + text + " offset " + offset);
   2520 
   2521    offset = text_width - step;
   2522    adjusted_offset = alignOffset(offset, text_width);
   2523    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2524    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2525    assert_equals(tm_position,
   2526                  doc_position,
   2527                  "for " + text + " offset " + offset);
   2528  }
   2529 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and directional-override.");
   2530 
   2531 test(t => {
   2532  const canvas = document.createElement('canvas');
   2533  canvas.width = 100;
   2534  canvas.height = 50;
   2535  const ctx = canvas.getContext('2d');
   2536 
   2537  function alignOffset(offset, width) {
   2538    if ('center' == 'center') {
   2539      offset -= width / 2;
   2540    } else if ('center' == 'right' ||
   2541             ('rtl' == 'ltr' && 'center' == 'end') ||
   2542             ('rtl' == 'rtl' && 'center' == 'start')) {
   2543      offset -= width;
   2544    }
   2545    return offset;
   2546  }
   2547 
   2548  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2549    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2550    const LTR_OVERRIDE = '\u202D';
   2551    const RTL_OVERRIDE = '\u202E';
   2552    const OVERRIDE_END = '\u202C';
   2553    if (direction_is_ltr) {
   2554      return LTR_OVERRIDE + text + OVERRIDE_END;
   2555    }
   2556    return RTL_OVERRIDE + text + OVERRIDE_END;
   2557  }
   2558 
   2559  function placeAndMeasureTextInDOM(text, text_width, point) {
   2560    const el = document.createElement("p");
   2561    el.innerHTML = text;
   2562    el.style.font = '50px sans-serif';
   2563    el.style.direction = 'rtl';
   2564    el.style.letterSpacing = '0px';
   2565    // Put the text top left to make offsets simpler.
   2566    el.style.padding = '0px';
   2567    el.style.margin = '0px';
   2568    el.style.position = 'absolute';
   2569    el.style.x = '0px';
   2570    el.style.y = '0px';
   2571    document.body.appendChild(el);
   2572    text_bound = el.getBoundingClientRect();
   2573    text_x = text_bound.x;
   2574    text_y = text_bound.y + text_bound.height / 2;
   2575 
   2576    // Offset to the requested point determined by textAlign and direction.
   2577    let text_align_dx = 0;
   2578    if ('center' == 'center') {
   2579      text_align_dx = text_width / 2;
   2580    } else if ('center' == 'right' ||
   2581             ('rtl' == 'ltr' && 'center' == 'end') ||
   2582             ('rtl' == 'rtl' && 'center' == 'start')) {
   2583      text_align_dx = text_width;
   2584    }
   2585    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2586    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2587 
   2588    document.body.removeChild(el);
   2589 
   2590    return position.offset;
   2591  }
   2592 
   2593  ctx.font = '50px sans-serif';
   2594  ctx.direction = 'rtl';
   2595  ctx.textAlign = 'center';
   2596  ctx.letterSpacing = '0px';
   2597 
   2598  const kTexts = [
   2599    'UNAVAILABLE',
   2600    '🏁🎢🏁',
   2601    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2602    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2603    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2604    '--abcd__'
   2605  ]
   2606 
   2607  for (text of kTexts) {
   2608    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2609    const tm = ctx.measureText(text);
   2610    text_width = tm.width;
   2611    step = 30;
   2612    if ('0px' == '10px') {
   2613      step = 40;
   2614    }
   2615 
   2616    offset = step;
   2617    adjusted_offset = alignOffset(offset, text_width);
   2618    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2619    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2620    assert_equals(tm_position,
   2621                  doc_position,
   2622                  "for " + text + " offset " + offset);
   2623 
   2624    offset = text_width / 2 - 10;
   2625    adjusted_offset = alignOffset(offset, text_width);
   2626    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2627    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2628    assert_equals(tm_position,
   2629                  doc_position,
   2630                  "for " + text + " offset " + offset);
   2631 
   2632    offset = text_width / 2 + 10;
   2633    adjusted_offset = alignOffset(offset, text_width);
   2634    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2635    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2636    assert_equals(tm_position,
   2637                  doc_position,
   2638                  "for " + text + " offset " + offset);
   2639 
   2640    offset = text_width - step;
   2641    adjusted_offset = alignOffset(offset, text_width);
   2642    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2643    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2644    assert_equals(tm_position,
   2645                  doc_position,
   2646                  "for " + text + " offset " + offset);
   2647  }
   2648 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and directional-override.");
   2649 
   2650 test(t => {
   2651  const canvas = document.createElement('canvas');
   2652  canvas.width = 100;
   2653  canvas.height = 50;
   2654  const ctx = canvas.getContext('2d');
   2655 
   2656  function alignOffset(offset, width) {
   2657    if ('right' == 'center') {
   2658      offset -= width / 2;
   2659    } else if ('right' == 'right' ||
   2660             ('ltr' == 'ltr' && 'right' == 'end') ||
   2661             ('ltr' == 'rtl' && 'right' == 'start')) {
   2662      offset -= width;
   2663    }
   2664    return offset;
   2665  }
   2666 
   2667  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2668    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2669    const LTR_OVERRIDE = '\u202D';
   2670    const RTL_OVERRIDE = '\u202E';
   2671    const OVERRIDE_END = '\u202C';
   2672    if (direction_is_ltr) {
   2673      return LTR_OVERRIDE + text + OVERRIDE_END;
   2674    }
   2675    return RTL_OVERRIDE + text + OVERRIDE_END;
   2676  }
   2677 
   2678  function placeAndMeasureTextInDOM(text, text_width, point) {
   2679    const el = document.createElement("p");
   2680    el.innerHTML = text;
   2681    el.style.font = '50px sans-serif';
   2682    el.style.direction = 'ltr';
   2683    el.style.letterSpacing = '0px';
   2684    // Put the text top left to make offsets simpler.
   2685    el.style.padding = '0px';
   2686    el.style.margin = '0px';
   2687    el.style.position = 'absolute';
   2688    el.style.x = '0px';
   2689    el.style.y = '0px';
   2690    document.body.appendChild(el);
   2691    text_bound = el.getBoundingClientRect();
   2692    text_x = text_bound.x;
   2693    text_y = text_bound.y + text_bound.height / 2;
   2694 
   2695    // Offset to the requested point determined by textAlign and direction.
   2696    let text_align_dx = 0;
   2697    if ('right' == 'center') {
   2698      text_align_dx = text_width / 2;
   2699    } else if ('right' == 'right' ||
   2700             ('ltr' == 'ltr' && 'right' == 'end') ||
   2701             ('ltr' == 'rtl' && 'right' == 'start')) {
   2702      text_align_dx = text_width;
   2703    }
   2704    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2705    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2706 
   2707    document.body.removeChild(el);
   2708 
   2709    return position.offset;
   2710  }
   2711 
   2712  ctx.font = '50px sans-serif';
   2713  ctx.direction = 'ltr';
   2714  ctx.textAlign = 'right';
   2715  ctx.letterSpacing = '0px';
   2716 
   2717  const kTexts = [
   2718    'UNAVAILABLE',
   2719    '🏁🎢🏁',
   2720    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2721    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2722    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2723    '--abcd__'
   2724  ]
   2725 
   2726  for (text of kTexts) {
   2727    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2728    const tm = ctx.measureText(text);
   2729    text_width = tm.width;
   2730    step = 30;
   2731    if ('0px' == '10px') {
   2732      step = 40;
   2733    }
   2734 
   2735    offset = step;
   2736    adjusted_offset = alignOffset(offset, text_width);
   2737    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2738    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2739    assert_equals(tm_position,
   2740                  doc_position,
   2741                  "for " + text + " offset " + offset);
   2742 
   2743    offset = text_width / 2 - 10;
   2744    adjusted_offset = alignOffset(offset, text_width);
   2745    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2746    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2747    assert_equals(tm_position,
   2748                  doc_position,
   2749                  "for " + text + " offset " + offset);
   2750 
   2751    offset = text_width / 2 + 10;
   2752    adjusted_offset = alignOffset(offset, text_width);
   2753    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2754    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2755    assert_equals(tm_position,
   2756                  doc_position,
   2757                  "for " + text + " offset " + offset);
   2758 
   2759    offset = text_width - step;
   2760    adjusted_offset = alignOffset(offset, text_width);
   2761    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2762    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2763    assert_equals(tm_position,
   2764                  doc_position,
   2765                  "for " + text + " offset " + offset);
   2766  }
   2767 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and directional-override.");
   2768 
   2769 test(t => {
   2770  const canvas = document.createElement('canvas');
   2771  canvas.width = 100;
   2772  canvas.height = 50;
   2773  const ctx = canvas.getContext('2d');
   2774 
   2775  function alignOffset(offset, width) {
   2776    if ('right' == 'center') {
   2777      offset -= width / 2;
   2778    } else if ('right' == 'right' ||
   2779             ('rtl' == 'ltr' && 'right' == 'end') ||
   2780             ('rtl' == 'rtl' && 'right' == 'start')) {
   2781      offset -= width;
   2782    }
   2783    return offset;
   2784  }
   2785 
   2786  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2787    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2788    const LTR_OVERRIDE = '\u202D';
   2789    const RTL_OVERRIDE = '\u202E';
   2790    const OVERRIDE_END = '\u202C';
   2791    if (direction_is_ltr) {
   2792      return LTR_OVERRIDE + text + OVERRIDE_END;
   2793    }
   2794    return RTL_OVERRIDE + text + OVERRIDE_END;
   2795  }
   2796 
   2797  function placeAndMeasureTextInDOM(text, text_width, point) {
   2798    const el = document.createElement("p");
   2799    el.innerHTML = text;
   2800    el.style.font = '50px sans-serif';
   2801    el.style.direction = 'rtl';
   2802    el.style.letterSpacing = '0px';
   2803    // Put the text top left to make offsets simpler.
   2804    el.style.padding = '0px';
   2805    el.style.margin = '0px';
   2806    el.style.position = 'absolute';
   2807    el.style.x = '0px';
   2808    el.style.y = '0px';
   2809    document.body.appendChild(el);
   2810    text_bound = el.getBoundingClientRect();
   2811    text_x = text_bound.x;
   2812    text_y = text_bound.y + text_bound.height / 2;
   2813 
   2814    // Offset to the requested point determined by textAlign and direction.
   2815    let text_align_dx = 0;
   2816    if ('right' == 'center') {
   2817      text_align_dx = text_width / 2;
   2818    } else if ('right' == 'right' ||
   2819             ('rtl' == 'ltr' && 'right' == 'end') ||
   2820             ('rtl' == 'rtl' && 'right' == 'start')) {
   2821      text_align_dx = text_width;
   2822    }
   2823    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2824    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2825 
   2826    document.body.removeChild(el);
   2827 
   2828    return position.offset;
   2829  }
   2830 
   2831  ctx.font = '50px sans-serif';
   2832  ctx.direction = 'rtl';
   2833  ctx.textAlign = 'right';
   2834  ctx.letterSpacing = '0px';
   2835 
   2836  const kTexts = [
   2837    'UNAVAILABLE',
   2838    '🏁🎢🏁',
   2839    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2840    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2841    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2842    '--abcd__'
   2843  ]
   2844 
   2845  for (text of kTexts) {
   2846    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2847    const tm = ctx.measureText(text);
   2848    text_width = tm.width;
   2849    step = 30;
   2850    if ('0px' == '10px') {
   2851      step = 40;
   2852    }
   2853 
   2854    offset = step;
   2855    adjusted_offset = alignOffset(offset, text_width);
   2856    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2857    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2858    assert_equals(tm_position,
   2859                  doc_position,
   2860                  "for " + text + " offset " + offset);
   2861 
   2862    offset = text_width / 2 - 10;
   2863    adjusted_offset = alignOffset(offset, text_width);
   2864    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2865    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2866    assert_equals(tm_position,
   2867                  doc_position,
   2868                  "for " + text + " offset " + offset);
   2869 
   2870    offset = text_width / 2 + 10;
   2871    adjusted_offset = alignOffset(offset, text_width);
   2872    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2873    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2874    assert_equals(tm_position,
   2875                  doc_position,
   2876                  "for " + text + " offset " + offset);
   2877 
   2878    offset = text_width - step;
   2879    adjusted_offset = alignOffset(offset, text_width);
   2880    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2881    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2882    assert_equals(tm_position,
   2883                  doc_position,
   2884                  "for " + text + " offset " + offset);
   2885  }
   2886 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and directional-override.");
   2887 
   2888 test(t => {
   2889  const canvas = document.createElement('canvas');
   2890  canvas.width = 100;
   2891  canvas.height = 50;
   2892  const ctx = canvas.getContext('2d');
   2893 
   2894  function alignOffset(offset, width) {
   2895    if ('start' == 'center') {
   2896      offset -= width / 2;
   2897    } else if ('start' == 'right' ||
   2898             ('ltr' == 'ltr' && 'start' == 'end') ||
   2899             ('ltr' == 'rtl' && 'start' == 'start')) {
   2900      offset -= width;
   2901    }
   2902    return offset;
   2903  }
   2904 
   2905  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   2906    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   2907    const LTR_OVERRIDE = '\u202D';
   2908    const RTL_OVERRIDE = '\u202E';
   2909    const OVERRIDE_END = '\u202C';
   2910    if (direction_is_ltr) {
   2911      return LTR_OVERRIDE + text + OVERRIDE_END;
   2912    }
   2913    return RTL_OVERRIDE + text + OVERRIDE_END;
   2914  }
   2915 
   2916  function placeAndMeasureTextInDOM(text, text_width, point) {
   2917    const el = document.createElement("p");
   2918    el.innerHTML = text;
   2919    el.style.font = '50px sans-serif';
   2920    el.style.direction = 'ltr';
   2921    el.style.letterSpacing = '0px';
   2922    // Put the text top left to make offsets simpler.
   2923    el.style.padding = '0px';
   2924    el.style.margin = '0px';
   2925    el.style.position = 'absolute';
   2926    el.style.x = '0px';
   2927    el.style.y = '0px';
   2928    document.body.appendChild(el);
   2929    text_bound = el.getBoundingClientRect();
   2930    text_x = text_bound.x;
   2931    text_y = text_bound.y + text_bound.height / 2;
   2932 
   2933    // Offset to the requested point determined by textAlign and direction.
   2934    let text_align_dx = 0;
   2935    if ('start' == 'center') {
   2936      text_align_dx = text_width / 2;
   2937    } else if ('start' == 'right' ||
   2938             ('ltr' == 'ltr' && 'start' == 'end') ||
   2939             ('ltr' == 'rtl' && 'start' == 'start')) {
   2940      text_align_dx = text_width;
   2941    }
   2942    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   2943    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   2944 
   2945    document.body.removeChild(el);
   2946 
   2947    return position.offset;
   2948  }
   2949 
   2950  ctx.font = '50px sans-serif';
   2951  ctx.direction = 'ltr';
   2952  ctx.textAlign = 'start';
   2953  ctx.letterSpacing = '0px';
   2954 
   2955  const kTexts = [
   2956    'UNAVAILABLE',
   2957    '🏁🎢🏁',
   2958    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   2959    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   2960    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   2961    '--abcd__'
   2962  ]
   2963 
   2964  for (text of kTexts) {
   2965    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   2966    const tm = ctx.measureText(text);
   2967    text_width = tm.width;
   2968    step = 30;
   2969    if ('0px' == '10px') {
   2970      step = 40;
   2971    }
   2972 
   2973    offset = step;
   2974    adjusted_offset = alignOffset(offset, text_width);
   2975    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2976    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2977    assert_equals(tm_position,
   2978                  doc_position,
   2979                  "for " + text + " offset " + offset);
   2980 
   2981    offset = text_width / 2 - 10;
   2982    adjusted_offset = alignOffset(offset, text_width);
   2983    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2984    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2985    assert_equals(tm_position,
   2986                  doc_position,
   2987                  "for " + text + " offset " + offset);
   2988 
   2989    offset = text_width / 2 + 10;
   2990    adjusted_offset = alignOffset(offset, text_width);
   2991    tm_position = tm.getIndexFromOffset(adjusted_offset);
   2992    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   2993    assert_equals(tm_position,
   2994                  doc_position,
   2995                  "for " + text + " offset " + offset);
   2996 
   2997    offset = text_width - step;
   2998    adjusted_offset = alignOffset(offset, text_width);
   2999    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3000    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3001    assert_equals(tm_position,
   3002                  doc_position,
   3003                  "for " + text + " offset " + offset);
   3004  }
   3005 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and directional-override.");
   3006 
   3007 test(t => {
   3008  const canvas = document.createElement('canvas');
   3009  canvas.width = 100;
   3010  canvas.height = 50;
   3011  const ctx = canvas.getContext('2d');
   3012 
   3013  function alignOffset(offset, width) {
   3014    if ('start' == 'center') {
   3015      offset -= width / 2;
   3016    } else if ('start' == 'right' ||
   3017             ('rtl' == 'ltr' && 'start' == 'end') ||
   3018             ('rtl' == 'rtl' && 'start' == 'start')) {
   3019      offset -= width;
   3020    }
   3021    return offset;
   3022  }
   3023 
   3024  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3025    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3026    const LTR_OVERRIDE = '\u202D';
   3027    const RTL_OVERRIDE = '\u202E';
   3028    const OVERRIDE_END = '\u202C';
   3029    if (direction_is_ltr) {
   3030      return LTR_OVERRIDE + text + OVERRIDE_END;
   3031    }
   3032    return RTL_OVERRIDE + text + OVERRIDE_END;
   3033  }
   3034 
   3035  function placeAndMeasureTextInDOM(text, text_width, point) {
   3036    const el = document.createElement("p");
   3037    el.innerHTML = text;
   3038    el.style.font = '50px sans-serif';
   3039    el.style.direction = 'rtl';
   3040    el.style.letterSpacing = '0px';
   3041    // Put the text top left to make offsets simpler.
   3042    el.style.padding = '0px';
   3043    el.style.margin = '0px';
   3044    el.style.position = 'absolute';
   3045    el.style.x = '0px';
   3046    el.style.y = '0px';
   3047    document.body.appendChild(el);
   3048    text_bound = el.getBoundingClientRect();
   3049    text_x = text_bound.x;
   3050    text_y = text_bound.y + text_bound.height / 2;
   3051 
   3052    // Offset to the requested point determined by textAlign and direction.
   3053    let text_align_dx = 0;
   3054    if ('start' == 'center') {
   3055      text_align_dx = text_width / 2;
   3056    } else if ('start' == 'right' ||
   3057             ('rtl' == 'ltr' && 'start' == 'end') ||
   3058             ('rtl' == 'rtl' && 'start' == 'start')) {
   3059      text_align_dx = text_width;
   3060    }
   3061    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3062    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3063 
   3064    document.body.removeChild(el);
   3065 
   3066    return position.offset;
   3067  }
   3068 
   3069  ctx.font = '50px sans-serif';
   3070  ctx.direction = 'rtl';
   3071  ctx.textAlign = 'start';
   3072  ctx.letterSpacing = '0px';
   3073 
   3074  const kTexts = [
   3075    'UNAVAILABLE',
   3076    '🏁🎢🏁',
   3077    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3078    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3079    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3080    '--abcd__'
   3081  ]
   3082 
   3083  for (text of kTexts) {
   3084    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3085    const tm = ctx.measureText(text);
   3086    text_width = tm.width;
   3087    step = 30;
   3088    if ('0px' == '10px') {
   3089      step = 40;
   3090    }
   3091 
   3092    offset = step;
   3093    adjusted_offset = alignOffset(offset, text_width);
   3094    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3095    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3096    assert_equals(tm_position,
   3097                  doc_position,
   3098                  "for " + text + " offset " + offset);
   3099 
   3100    offset = text_width / 2 - 10;
   3101    adjusted_offset = alignOffset(offset, text_width);
   3102    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3103    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3104    assert_equals(tm_position,
   3105                  doc_position,
   3106                  "for " + text + " offset " + offset);
   3107 
   3108    offset = text_width / 2 + 10;
   3109    adjusted_offset = alignOffset(offset, text_width);
   3110    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3111    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3112    assert_equals(tm_position,
   3113                  doc_position,
   3114                  "for " + text + " offset " + offset);
   3115 
   3116    offset = text_width - step;
   3117    adjusted_offset = alignOffset(offset, text_width);
   3118    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3119    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3120    assert_equals(tm_position,
   3121                  doc_position,
   3122                  "for " + text + " offset " + offset);
   3123  }
   3124 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and directional-override.");
   3125 
   3126 test(t => {
   3127  const canvas = document.createElement('canvas');
   3128  canvas.width = 100;
   3129  canvas.height = 50;
   3130  const ctx = canvas.getContext('2d');
   3131 
   3132  function alignOffset(offset, width) {
   3133    if ('end' == 'center') {
   3134      offset -= width / 2;
   3135    } else if ('end' == 'right' ||
   3136             ('ltr' == 'ltr' && 'end' == 'end') ||
   3137             ('ltr' == 'rtl' && 'end' == 'start')) {
   3138      offset -= width;
   3139    }
   3140    return offset;
   3141  }
   3142 
   3143  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3144    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3145    const LTR_OVERRIDE = '\u202D';
   3146    const RTL_OVERRIDE = '\u202E';
   3147    const OVERRIDE_END = '\u202C';
   3148    if (direction_is_ltr) {
   3149      return LTR_OVERRIDE + text + OVERRIDE_END;
   3150    }
   3151    return RTL_OVERRIDE + text + OVERRIDE_END;
   3152  }
   3153 
   3154  function placeAndMeasureTextInDOM(text, text_width, point) {
   3155    const el = document.createElement("p");
   3156    el.innerHTML = text;
   3157    el.style.font = '50px sans-serif';
   3158    el.style.direction = 'ltr';
   3159    el.style.letterSpacing = '0px';
   3160    // Put the text top left to make offsets simpler.
   3161    el.style.padding = '0px';
   3162    el.style.margin = '0px';
   3163    el.style.position = 'absolute';
   3164    el.style.x = '0px';
   3165    el.style.y = '0px';
   3166    document.body.appendChild(el);
   3167    text_bound = el.getBoundingClientRect();
   3168    text_x = text_bound.x;
   3169    text_y = text_bound.y + text_bound.height / 2;
   3170 
   3171    // Offset to the requested point determined by textAlign and direction.
   3172    let text_align_dx = 0;
   3173    if ('end' == 'center') {
   3174      text_align_dx = text_width / 2;
   3175    } else if ('end' == 'right' ||
   3176             ('ltr' == 'ltr' && 'end' == 'end') ||
   3177             ('ltr' == 'rtl' && 'end' == 'start')) {
   3178      text_align_dx = text_width;
   3179    }
   3180    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3181    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3182 
   3183    document.body.removeChild(el);
   3184 
   3185    return position.offset;
   3186  }
   3187 
   3188  ctx.font = '50px sans-serif';
   3189  ctx.direction = 'ltr';
   3190  ctx.textAlign = 'end';
   3191  ctx.letterSpacing = '0px';
   3192 
   3193  const kTexts = [
   3194    'UNAVAILABLE',
   3195    '🏁🎢🏁',
   3196    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3197    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3198    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3199    '--abcd__'
   3200  ]
   3201 
   3202  for (text of kTexts) {
   3203    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3204    const tm = ctx.measureText(text);
   3205    text_width = tm.width;
   3206    step = 30;
   3207    if ('0px' == '10px') {
   3208      step = 40;
   3209    }
   3210 
   3211    offset = step;
   3212    adjusted_offset = alignOffset(offset, text_width);
   3213    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3214    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3215    assert_equals(tm_position,
   3216                  doc_position,
   3217                  "for " + text + " offset " + offset);
   3218 
   3219    offset = text_width / 2 - 10;
   3220    adjusted_offset = alignOffset(offset, text_width);
   3221    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3222    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3223    assert_equals(tm_position,
   3224                  doc_position,
   3225                  "for " + text + " offset " + offset);
   3226 
   3227    offset = text_width / 2 + 10;
   3228    adjusted_offset = alignOffset(offset, text_width);
   3229    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3230    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3231    assert_equals(tm_position,
   3232                  doc_position,
   3233                  "for " + text + " offset " + offset);
   3234 
   3235    offset = text_width - step;
   3236    adjusted_offset = alignOffset(offset, text_width);
   3237    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3238    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3239    assert_equals(tm_position,
   3240                  doc_position,
   3241                  "for " + text + " offset " + offset);
   3242  }
   3243 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and directional-override.");
   3244 
   3245 test(t => {
   3246  const canvas = document.createElement('canvas');
   3247  canvas.width = 100;
   3248  canvas.height = 50;
   3249  const ctx = canvas.getContext('2d');
   3250 
   3251  function alignOffset(offset, width) {
   3252    if ('end' == 'center') {
   3253      offset -= width / 2;
   3254    } else if ('end' == 'right' ||
   3255             ('rtl' == 'ltr' && 'end' == 'end') ||
   3256             ('rtl' == 'rtl' && 'end' == 'start')) {
   3257      offset -= width;
   3258    }
   3259    return offset;
   3260  }
   3261 
   3262  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3263    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3264    const LTR_OVERRIDE = '\u202D';
   3265    const RTL_OVERRIDE = '\u202E';
   3266    const OVERRIDE_END = '\u202C';
   3267    if (direction_is_ltr) {
   3268      return LTR_OVERRIDE + text + OVERRIDE_END;
   3269    }
   3270    return RTL_OVERRIDE + text + OVERRIDE_END;
   3271  }
   3272 
   3273  function placeAndMeasureTextInDOM(text, text_width, point) {
   3274    const el = document.createElement("p");
   3275    el.innerHTML = text;
   3276    el.style.font = '50px sans-serif';
   3277    el.style.direction = 'rtl';
   3278    el.style.letterSpacing = '0px';
   3279    // Put the text top left to make offsets simpler.
   3280    el.style.padding = '0px';
   3281    el.style.margin = '0px';
   3282    el.style.position = 'absolute';
   3283    el.style.x = '0px';
   3284    el.style.y = '0px';
   3285    document.body.appendChild(el);
   3286    text_bound = el.getBoundingClientRect();
   3287    text_x = text_bound.x;
   3288    text_y = text_bound.y + text_bound.height / 2;
   3289 
   3290    // Offset to the requested point determined by textAlign and direction.
   3291    let text_align_dx = 0;
   3292    if ('end' == 'center') {
   3293      text_align_dx = text_width / 2;
   3294    } else if ('end' == 'right' ||
   3295             ('rtl' == 'ltr' && 'end' == 'end') ||
   3296             ('rtl' == 'rtl' && 'end' == 'start')) {
   3297      text_align_dx = text_width;
   3298    }
   3299    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3300    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3301 
   3302    document.body.removeChild(el);
   3303 
   3304    return position.offset;
   3305  }
   3306 
   3307  ctx.font = '50px sans-serif';
   3308  ctx.direction = 'rtl';
   3309  ctx.textAlign = 'end';
   3310  ctx.letterSpacing = '0px';
   3311 
   3312  const kTexts = [
   3313    'UNAVAILABLE',
   3314    '🏁🎢🏁',
   3315    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3316    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3317    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3318    '--abcd__'
   3319  ]
   3320 
   3321  for (text of kTexts) {
   3322    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3323    const tm = ctx.measureText(text);
   3324    text_width = tm.width;
   3325    step = 30;
   3326    if ('0px' == '10px') {
   3327      step = 40;
   3328    }
   3329 
   3330    offset = step;
   3331    adjusted_offset = alignOffset(offset, text_width);
   3332    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3333    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3334    assert_equals(tm_position,
   3335                  doc_position,
   3336                  "for " + text + " offset " + offset);
   3337 
   3338    offset = text_width / 2 - 10;
   3339    adjusted_offset = alignOffset(offset, text_width);
   3340    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3341    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3342    assert_equals(tm_position,
   3343                  doc_position,
   3344                  "for " + text + " offset " + offset);
   3345 
   3346    offset = text_width / 2 + 10;
   3347    adjusted_offset = alignOffset(offset, text_width);
   3348    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3349    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3350    assert_equals(tm_position,
   3351                  doc_position,
   3352                  "for " + text + " offset " + offset);
   3353 
   3354    offset = text_width - step;
   3355    adjusted_offset = alignOffset(offset, text_width);
   3356    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3357    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3358    assert_equals(tm_position,
   3359                  doc_position,
   3360                  "for " + text + " offset " + offset);
   3361  }
   3362 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and directional-override.");
   3363 
   3364 test(t => {
   3365  const canvas = document.createElement('canvas');
   3366  canvas.width = 100;
   3367  canvas.height = 50;
   3368  const ctx = canvas.getContext('2d');
   3369 
   3370  function alignOffset(offset, width) {
   3371    if ('left' == 'center') {
   3372      offset -= width / 2;
   3373    } else if ('left' == 'right' ||
   3374             ('ltr' == 'ltr' && 'left' == 'end') ||
   3375             ('ltr' == 'rtl' && 'left' == 'start')) {
   3376      offset -= width;
   3377    }
   3378    return offset;
   3379  }
   3380 
   3381  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3382    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3383    const LTR_OVERRIDE = '\u202D';
   3384    const RTL_OVERRIDE = '\u202E';
   3385    const OVERRIDE_END = '\u202C';
   3386    if (direction_is_ltr) {
   3387      return LTR_OVERRIDE + text + OVERRIDE_END;
   3388    }
   3389    return RTL_OVERRIDE + text + OVERRIDE_END;
   3390  }
   3391 
   3392  function placeAndMeasureTextInDOM(text, text_width, point) {
   3393    const el = document.createElement("p");
   3394    el.innerHTML = text;
   3395    el.style.font = '50px sans-serif';
   3396    el.style.direction = 'ltr';
   3397    el.style.letterSpacing = '10px';
   3398    // Put the text top left to make offsets simpler.
   3399    el.style.padding = '0px';
   3400    el.style.margin = '0px';
   3401    el.style.position = 'absolute';
   3402    el.style.x = '0px';
   3403    el.style.y = '0px';
   3404    document.body.appendChild(el);
   3405    text_bound = el.getBoundingClientRect();
   3406    text_x = text_bound.x;
   3407    text_y = text_bound.y + text_bound.height / 2;
   3408 
   3409    // Offset to the requested point determined by textAlign and direction.
   3410    let text_align_dx = 0;
   3411    if ('left' == 'center') {
   3412      text_align_dx = text_width / 2;
   3413    } else if ('left' == 'right' ||
   3414             ('ltr' == 'ltr' && 'left' == 'end') ||
   3415             ('ltr' == 'rtl' && 'left' == 'start')) {
   3416      text_align_dx = text_width;
   3417    }
   3418    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3419    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3420 
   3421    document.body.removeChild(el);
   3422 
   3423    return position.offset;
   3424  }
   3425 
   3426  ctx.font = '50px sans-serif';
   3427  ctx.direction = 'ltr';
   3428  ctx.textAlign = 'left';
   3429  ctx.letterSpacing = '10px';
   3430 
   3431  const kTexts = [
   3432    'UNAVAILABLE',
   3433    '🏁🎢🏁',
   3434    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3435    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3436    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3437    '--abcd__'
   3438  ]
   3439 
   3440  for (text of kTexts) {
   3441    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3442    const tm = ctx.measureText(text);
   3443    text_width = tm.width;
   3444    step = 30;
   3445    if ('10px' == '10px') {
   3446      step = 40;
   3447    }
   3448 
   3449    offset = step;
   3450    adjusted_offset = alignOffset(offset, text_width);
   3451    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3452    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3453    assert_equals(tm_position,
   3454                  doc_position,
   3455                  "for " + text + " offset " + offset);
   3456 
   3457    offset = text_width / 2 - 10;
   3458    adjusted_offset = alignOffset(offset, text_width);
   3459    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3460    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3461    assert_equals(tm_position,
   3462                  doc_position,
   3463                  "for " + text + " offset " + offset);
   3464 
   3465    offset = text_width / 2 + 10;
   3466    adjusted_offset = alignOffset(offset, text_width);
   3467    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3468    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3469    assert_equals(tm_position,
   3470                  doc_position,
   3471                  "for " + text + " offset " + offset);
   3472 
   3473    offset = text_width - step;
   3474    adjusted_offset = alignOffset(offset, text_width);
   3475    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3476    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3477    assert_equals(tm_position,
   3478                  doc_position,
   3479                  "for " + text + " offset " + offset);
   3480  }
   3481 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and directional-override.");
   3482 
   3483 test(t => {
   3484  const canvas = document.createElement('canvas');
   3485  canvas.width = 100;
   3486  canvas.height = 50;
   3487  const ctx = canvas.getContext('2d');
   3488 
   3489  function alignOffset(offset, width) {
   3490    if ('left' == 'center') {
   3491      offset -= width / 2;
   3492    } else if ('left' == 'right' ||
   3493             ('rtl' == 'ltr' && 'left' == 'end') ||
   3494             ('rtl' == 'rtl' && 'left' == 'start')) {
   3495      offset -= width;
   3496    }
   3497    return offset;
   3498  }
   3499 
   3500  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3501    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3502    const LTR_OVERRIDE = '\u202D';
   3503    const RTL_OVERRIDE = '\u202E';
   3504    const OVERRIDE_END = '\u202C';
   3505    if (direction_is_ltr) {
   3506      return LTR_OVERRIDE + text + OVERRIDE_END;
   3507    }
   3508    return RTL_OVERRIDE + text + OVERRIDE_END;
   3509  }
   3510 
   3511  function placeAndMeasureTextInDOM(text, text_width, point) {
   3512    const el = document.createElement("p");
   3513    el.innerHTML = text;
   3514    el.style.font = '50px sans-serif';
   3515    el.style.direction = 'rtl';
   3516    el.style.letterSpacing = '10px';
   3517    // Put the text top left to make offsets simpler.
   3518    el.style.padding = '0px';
   3519    el.style.margin = '0px';
   3520    el.style.position = 'absolute';
   3521    el.style.x = '0px';
   3522    el.style.y = '0px';
   3523    document.body.appendChild(el);
   3524    text_bound = el.getBoundingClientRect();
   3525    text_x = text_bound.x;
   3526    text_y = text_bound.y + text_bound.height / 2;
   3527 
   3528    // Offset to the requested point determined by textAlign and direction.
   3529    let text_align_dx = 0;
   3530    if ('left' == 'center') {
   3531      text_align_dx = text_width / 2;
   3532    } else if ('left' == 'right' ||
   3533             ('rtl' == 'ltr' && 'left' == 'end') ||
   3534             ('rtl' == 'rtl' && 'left' == 'start')) {
   3535      text_align_dx = text_width;
   3536    }
   3537    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3538    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3539 
   3540    document.body.removeChild(el);
   3541 
   3542    return position.offset;
   3543  }
   3544 
   3545  ctx.font = '50px sans-serif';
   3546  ctx.direction = 'rtl';
   3547  ctx.textAlign = 'left';
   3548  ctx.letterSpacing = '10px';
   3549 
   3550  const kTexts = [
   3551    'UNAVAILABLE',
   3552    '🏁🎢🏁',
   3553    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3554    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3555    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3556    '--abcd__'
   3557  ]
   3558 
   3559  for (text of kTexts) {
   3560    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3561    const tm = ctx.measureText(text);
   3562    text_width = tm.width;
   3563    step = 30;
   3564    if ('10px' == '10px') {
   3565      step = 40;
   3566    }
   3567 
   3568    offset = step;
   3569    adjusted_offset = alignOffset(offset, text_width);
   3570    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3571    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3572    assert_equals(tm_position,
   3573                  doc_position,
   3574                  "for " + text + " offset " + offset);
   3575 
   3576    offset = text_width / 2 - 10;
   3577    adjusted_offset = alignOffset(offset, text_width);
   3578    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3579    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3580    assert_equals(tm_position,
   3581                  doc_position,
   3582                  "for " + text + " offset " + offset);
   3583 
   3584    offset = text_width / 2 + 10;
   3585    adjusted_offset = alignOffset(offset, text_width);
   3586    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3587    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3588    assert_equals(tm_position,
   3589                  doc_position,
   3590                  "for " + text + " offset " + offset);
   3591 
   3592    offset = text_width - step;
   3593    adjusted_offset = alignOffset(offset, text_width);
   3594    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3595    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3596    assert_equals(tm_position,
   3597                  doc_position,
   3598                  "for " + text + " offset " + offset);
   3599  }
   3600 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and directional-override.");
   3601 
   3602 test(t => {
   3603  const canvas = document.createElement('canvas');
   3604  canvas.width = 100;
   3605  canvas.height = 50;
   3606  const ctx = canvas.getContext('2d');
   3607 
   3608  function alignOffset(offset, width) {
   3609    if ('center' == 'center') {
   3610      offset -= width / 2;
   3611    } else if ('center' == 'right' ||
   3612             ('ltr' == 'ltr' && 'center' == 'end') ||
   3613             ('ltr' == 'rtl' && 'center' == 'start')) {
   3614      offset -= width;
   3615    }
   3616    return offset;
   3617  }
   3618 
   3619  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3620    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3621    const LTR_OVERRIDE = '\u202D';
   3622    const RTL_OVERRIDE = '\u202E';
   3623    const OVERRIDE_END = '\u202C';
   3624    if (direction_is_ltr) {
   3625      return LTR_OVERRIDE + text + OVERRIDE_END;
   3626    }
   3627    return RTL_OVERRIDE + text + OVERRIDE_END;
   3628  }
   3629 
   3630  function placeAndMeasureTextInDOM(text, text_width, point) {
   3631    const el = document.createElement("p");
   3632    el.innerHTML = text;
   3633    el.style.font = '50px sans-serif';
   3634    el.style.direction = 'ltr';
   3635    el.style.letterSpacing = '10px';
   3636    // Put the text top left to make offsets simpler.
   3637    el.style.padding = '0px';
   3638    el.style.margin = '0px';
   3639    el.style.position = 'absolute';
   3640    el.style.x = '0px';
   3641    el.style.y = '0px';
   3642    document.body.appendChild(el);
   3643    text_bound = el.getBoundingClientRect();
   3644    text_x = text_bound.x;
   3645    text_y = text_bound.y + text_bound.height / 2;
   3646 
   3647    // Offset to the requested point determined by textAlign and direction.
   3648    let text_align_dx = 0;
   3649    if ('center' == 'center') {
   3650      text_align_dx = text_width / 2;
   3651    } else if ('center' == 'right' ||
   3652             ('ltr' == 'ltr' && 'center' == 'end') ||
   3653             ('ltr' == 'rtl' && 'center' == 'start')) {
   3654      text_align_dx = text_width;
   3655    }
   3656    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3657    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3658 
   3659    document.body.removeChild(el);
   3660 
   3661    return position.offset;
   3662  }
   3663 
   3664  ctx.font = '50px sans-serif';
   3665  ctx.direction = 'ltr';
   3666  ctx.textAlign = 'center';
   3667  ctx.letterSpacing = '10px';
   3668 
   3669  const kTexts = [
   3670    'UNAVAILABLE',
   3671    '🏁🎢🏁',
   3672    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3673    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3674    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3675    '--abcd__'
   3676  ]
   3677 
   3678  for (text of kTexts) {
   3679    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3680    const tm = ctx.measureText(text);
   3681    text_width = tm.width;
   3682    step = 30;
   3683    if ('10px' == '10px') {
   3684      step = 40;
   3685    }
   3686 
   3687    offset = step;
   3688    adjusted_offset = alignOffset(offset, text_width);
   3689    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3690    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3691    assert_equals(tm_position,
   3692                  doc_position,
   3693                  "for " + text + " offset " + offset);
   3694 
   3695    offset = text_width / 2 - 10;
   3696    adjusted_offset = alignOffset(offset, text_width);
   3697    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3698    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3699    assert_equals(tm_position,
   3700                  doc_position,
   3701                  "for " + text + " offset " + offset);
   3702 
   3703    offset = text_width / 2 + 10;
   3704    adjusted_offset = alignOffset(offset, text_width);
   3705    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3706    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3707    assert_equals(tm_position,
   3708                  doc_position,
   3709                  "for " + text + " offset " + offset);
   3710 
   3711    offset = text_width - step;
   3712    adjusted_offset = alignOffset(offset, text_width);
   3713    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3714    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3715    assert_equals(tm_position,
   3716                  doc_position,
   3717                  "for " + text + " offset " + offset);
   3718  }
   3719 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and directional-override.");
   3720 
   3721 test(t => {
   3722  const canvas = document.createElement('canvas');
   3723  canvas.width = 100;
   3724  canvas.height = 50;
   3725  const ctx = canvas.getContext('2d');
   3726 
   3727  function alignOffset(offset, width) {
   3728    if ('center' == 'center') {
   3729      offset -= width / 2;
   3730    } else if ('center' == 'right' ||
   3731             ('rtl' == 'ltr' && 'center' == 'end') ||
   3732             ('rtl' == 'rtl' && 'center' == 'start')) {
   3733      offset -= width;
   3734    }
   3735    return offset;
   3736  }
   3737 
   3738  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3739    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3740    const LTR_OVERRIDE = '\u202D';
   3741    const RTL_OVERRIDE = '\u202E';
   3742    const OVERRIDE_END = '\u202C';
   3743    if (direction_is_ltr) {
   3744      return LTR_OVERRIDE + text + OVERRIDE_END;
   3745    }
   3746    return RTL_OVERRIDE + text + OVERRIDE_END;
   3747  }
   3748 
   3749  function placeAndMeasureTextInDOM(text, text_width, point) {
   3750    const el = document.createElement("p");
   3751    el.innerHTML = text;
   3752    el.style.font = '50px sans-serif';
   3753    el.style.direction = 'rtl';
   3754    el.style.letterSpacing = '10px';
   3755    // Put the text top left to make offsets simpler.
   3756    el.style.padding = '0px';
   3757    el.style.margin = '0px';
   3758    el.style.position = 'absolute';
   3759    el.style.x = '0px';
   3760    el.style.y = '0px';
   3761    document.body.appendChild(el);
   3762    text_bound = el.getBoundingClientRect();
   3763    text_x = text_bound.x;
   3764    text_y = text_bound.y + text_bound.height / 2;
   3765 
   3766    // Offset to the requested point determined by textAlign and direction.
   3767    let text_align_dx = 0;
   3768    if ('center' == 'center') {
   3769      text_align_dx = text_width / 2;
   3770    } else if ('center' == 'right' ||
   3771             ('rtl' == 'ltr' && 'center' == 'end') ||
   3772             ('rtl' == 'rtl' && 'center' == 'start')) {
   3773      text_align_dx = text_width;
   3774    }
   3775    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3776    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3777 
   3778    document.body.removeChild(el);
   3779 
   3780    return position.offset;
   3781  }
   3782 
   3783  ctx.font = '50px sans-serif';
   3784  ctx.direction = 'rtl';
   3785  ctx.textAlign = 'center';
   3786  ctx.letterSpacing = '10px';
   3787 
   3788  const kTexts = [
   3789    'UNAVAILABLE',
   3790    '🏁🎢🏁',
   3791    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3792    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3793    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3794    '--abcd__'
   3795  ]
   3796 
   3797  for (text of kTexts) {
   3798    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3799    const tm = ctx.measureText(text);
   3800    text_width = tm.width;
   3801    step = 30;
   3802    if ('10px' == '10px') {
   3803      step = 40;
   3804    }
   3805 
   3806    offset = step;
   3807    adjusted_offset = alignOffset(offset, text_width);
   3808    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3809    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3810    assert_equals(tm_position,
   3811                  doc_position,
   3812                  "for " + text + " offset " + offset);
   3813 
   3814    offset = text_width / 2 - 10;
   3815    adjusted_offset = alignOffset(offset, text_width);
   3816    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3817    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3818    assert_equals(tm_position,
   3819                  doc_position,
   3820                  "for " + text + " offset " + offset);
   3821 
   3822    offset = text_width / 2 + 10;
   3823    adjusted_offset = alignOffset(offset, text_width);
   3824    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3825    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3826    assert_equals(tm_position,
   3827                  doc_position,
   3828                  "for " + text + " offset " + offset);
   3829 
   3830    offset = text_width - step;
   3831    adjusted_offset = alignOffset(offset, text_width);
   3832    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3833    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3834    assert_equals(tm_position,
   3835                  doc_position,
   3836                  "for " + text + " offset " + offset);
   3837  }
   3838 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and directional-override.");
   3839 
   3840 test(t => {
   3841  const canvas = document.createElement('canvas');
   3842  canvas.width = 100;
   3843  canvas.height = 50;
   3844  const ctx = canvas.getContext('2d');
   3845 
   3846  function alignOffset(offset, width) {
   3847    if ('right' == 'center') {
   3848      offset -= width / 2;
   3849    } else if ('right' == 'right' ||
   3850             ('ltr' == 'ltr' && 'right' == 'end') ||
   3851             ('ltr' == 'rtl' && 'right' == 'start')) {
   3852      offset -= width;
   3853    }
   3854    return offset;
   3855  }
   3856 
   3857  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3858    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3859    const LTR_OVERRIDE = '\u202D';
   3860    const RTL_OVERRIDE = '\u202E';
   3861    const OVERRIDE_END = '\u202C';
   3862    if (direction_is_ltr) {
   3863      return LTR_OVERRIDE + text + OVERRIDE_END;
   3864    }
   3865    return RTL_OVERRIDE + text + OVERRIDE_END;
   3866  }
   3867 
   3868  function placeAndMeasureTextInDOM(text, text_width, point) {
   3869    const el = document.createElement("p");
   3870    el.innerHTML = text;
   3871    el.style.font = '50px sans-serif';
   3872    el.style.direction = 'ltr';
   3873    el.style.letterSpacing = '10px';
   3874    // Put the text top left to make offsets simpler.
   3875    el.style.padding = '0px';
   3876    el.style.margin = '0px';
   3877    el.style.position = 'absolute';
   3878    el.style.x = '0px';
   3879    el.style.y = '0px';
   3880    document.body.appendChild(el);
   3881    text_bound = el.getBoundingClientRect();
   3882    text_x = text_bound.x;
   3883    text_y = text_bound.y + text_bound.height / 2;
   3884 
   3885    // Offset to the requested point determined by textAlign and direction.
   3886    let text_align_dx = 0;
   3887    if ('right' == 'center') {
   3888      text_align_dx = text_width / 2;
   3889    } else if ('right' == 'right' ||
   3890             ('ltr' == 'ltr' && 'right' == 'end') ||
   3891             ('ltr' == 'rtl' && 'right' == 'start')) {
   3892      text_align_dx = text_width;
   3893    }
   3894    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   3895    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   3896 
   3897    document.body.removeChild(el);
   3898 
   3899    return position.offset;
   3900  }
   3901 
   3902  ctx.font = '50px sans-serif';
   3903  ctx.direction = 'ltr';
   3904  ctx.textAlign = 'right';
   3905  ctx.letterSpacing = '10px';
   3906 
   3907  const kTexts = [
   3908    'UNAVAILABLE',
   3909    '🏁🎢🏁',
   3910    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   3911    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   3912    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   3913    '--abcd__'
   3914  ]
   3915 
   3916  for (text of kTexts) {
   3917    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   3918    const tm = ctx.measureText(text);
   3919    text_width = tm.width;
   3920    step = 30;
   3921    if ('10px' == '10px') {
   3922      step = 40;
   3923    }
   3924 
   3925    offset = step;
   3926    adjusted_offset = alignOffset(offset, text_width);
   3927    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3928    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3929    assert_equals(tm_position,
   3930                  doc_position,
   3931                  "for " + text + " offset " + offset);
   3932 
   3933    offset = text_width / 2 - 10;
   3934    adjusted_offset = alignOffset(offset, text_width);
   3935    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3936    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3937    assert_equals(tm_position,
   3938                  doc_position,
   3939                  "for " + text + " offset " + offset);
   3940 
   3941    offset = text_width / 2 + 10;
   3942    adjusted_offset = alignOffset(offset, text_width);
   3943    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3944    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3945    assert_equals(tm_position,
   3946                  doc_position,
   3947                  "for " + text + " offset " + offset);
   3948 
   3949    offset = text_width - step;
   3950    adjusted_offset = alignOffset(offset, text_width);
   3951    tm_position = tm.getIndexFromOffset(adjusted_offset);
   3952    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   3953    assert_equals(tm_position,
   3954                  doc_position,
   3955                  "for " + text + " offset " + offset);
   3956  }
   3957 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and directional-override.");
   3958 
   3959 test(t => {
   3960  const canvas = document.createElement('canvas');
   3961  canvas.width = 100;
   3962  canvas.height = 50;
   3963  const ctx = canvas.getContext('2d');
   3964 
   3965  function alignOffset(offset, width) {
   3966    if ('right' == 'center') {
   3967      offset -= width / 2;
   3968    } else if ('right' == 'right' ||
   3969             ('rtl' == 'ltr' && 'right' == 'end') ||
   3970             ('rtl' == 'rtl' && 'right' == 'start')) {
   3971      offset -= width;
   3972    }
   3973    return offset;
   3974  }
   3975 
   3976  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   3977    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   3978    const LTR_OVERRIDE = '\u202D';
   3979    const RTL_OVERRIDE = '\u202E';
   3980    const OVERRIDE_END = '\u202C';
   3981    if (direction_is_ltr) {
   3982      return LTR_OVERRIDE + text + OVERRIDE_END;
   3983    }
   3984    return RTL_OVERRIDE + text + OVERRIDE_END;
   3985  }
   3986 
   3987  function placeAndMeasureTextInDOM(text, text_width, point) {
   3988    const el = document.createElement("p");
   3989    el.innerHTML = text;
   3990    el.style.font = '50px sans-serif';
   3991    el.style.direction = 'rtl';
   3992    el.style.letterSpacing = '10px';
   3993    // Put the text top left to make offsets simpler.
   3994    el.style.padding = '0px';
   3995    el.style.margin = '0px';
   3996    el.style.position = 'absolute';
   3997    el.style.x = '0px';
   3998    el.style.y = '0px';
   3999    document.body.appendChild(el);
   4000    text_bound = el.getBoundingClientRect();
   4001    text_x = text_bound.x;
   4002    text_y = text_bound.y + text_bound.height / 2;
   4003 
   4004    // Offset to the requested point determined by textAlign and direction.
   4005    let text_align_dx = 0;
   4006    if ('right' == 'center') {
   4007      text_align_dx = text_width / 2;
   4008    } else if ('right' == 'right' ||
   4009             ('rtl' == 'ltr' && 'right' == 'end') ||
   4010             ('rtl' == 'rtl' && 'right' == 'start')) {
   4011      text_align_dx = text_width;
   4012    }
   4013    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   4014    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   4015 
   4016    document.body.removeChild(el);
   4017 
   4018    return position.offset;
   4019  }
   4020 
   4021  ctx.font = '50px sans-serif';
   4022  ctx.direction = 'rtl';
   4023  ctx.textAlign = 'right';
   4024  ctx.letterSpacing = '10px';
   4025 
   4026  const kTexts = [
   4027    'UNAVAILABLE',
   4028    '🏁🎢🏁',
   4029    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   4030    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   4031    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   4032    '--abcd__'
   4033  ]
   4034 
   4035  for (text of kTexts) {
   4036    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   4037    const tm = ctx.measureText(text);
   4038    text_width = tm.width;
   4039    step = 30;
   4040    if ('10px' == '10px') {
   4041      step = 40;
   4042    }
   4043 
   4044    offset = step;
   4045    adjusted_offset = alignOffset(offset, text_width);
   4046    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4047    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4048    assert_equals(tm_position,
   4049                  doc_position,
   4050                  "for " + text + " offset " + offset);
   4051 
   4052    offset = text_width / 2 - 10;
   4053    adjusted_offset = alignOffset(offset, text_width);
   4054    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4055    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4056    assert_equals(tm_position,
   4057                  doc_position,
   4058                  "for " + text + " offset " + offset);
   4059 
   4060    offset = text_width / 2 + 10;
   4061    adjusted_offset = alignOffset(offset, text_width);
   4062    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4063    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4064    assert_equals(tm_position,
   4065                  doc_position,
   4066                  "for " + text + " offset " + offset);
   4067 
   4068    offset = text_width - step;
   4069    adjusted_offset = alignOffset(offset, text_width);
   4070    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4071    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4072    assert_equals(tm_position,
   4073                  doc_position,
   4074                  "for " + text + " offset " + offset);
   4075  }
   4076 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and directional-override.");
   4077 
   4078 test(t => {
   4079  const canvas = document.createElement('canvas');
   4080  canvas.width = 100;
   4081  canvas.height = 50;
   4082  const ctx = canvas.getContext('2d');
   4083 
   4084  function alignOffset(offset, width) {
   4085    if ('start' == 'center') {
   4086      offset -= width / 2;
   4087    } else if ('start' == 'right' ||
   4088             ('ltr' == 'ltr' && 'start' == 'end') ||
   4089             ('ltr' == 'rtl' && 'start' == 'start')) {
   4090      offset -= width;
   4091    }
   4092    return offset;
   4093  }
   4094 
   4095  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   4096    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   4097    const LTR_OVERRIDE = '\u202D';
   4098    const RTL_OVERRIDE = '\u202E';
   4099    const OVERRIDE_END = '\u202C';
   4100    if (direction_is_ltr) {
   4101      return LTR_OVERRIDE + text + OVERRIDE_END;
   4102    }
   4103    return RTL_OVERRIDE + text + OVERRIDE_END;
   4104  }
   4105 
   4106  function placeAndMeasureTextInDOM(text, text_width, point) {
   4107    const el = document.createElement("p");
   4108    el.innerHTML = text;
   4109    el.style.font = '50px sans-serif';
   4110    el.style.direction = 'ltr';
   4111    el.style.letterSpacing = '10px';
   4112    // Put the text top left to make offsets simpler.
   4113    el.style.padding = '0px';
   4114    el.style.margin = '0px';
   4115    el.style.position = 'absolute';
   4116    el.style.x = '0px';
   4117    el.style.y = '0px';
   4118    document.body.appendChild(el);
   4119    text_bound = el.getBoundingClientRect();
   4120    text_x = text_bound.x;
   4121    text_y = text_bound.y + text_bound.height / 2;
   4122 
   4123    // Offset to the requested point determined by textAlign and direction.
   4124    let text_align_dx = 0;
   4125    if ('start' == 'center') {
   4126      text_align_dx = text_width / 2;
   4127    } else if ('start' == 'right' ||
   4128             ('ltr' == 'ltr' && 'start' == 'end') ||
   4129             ('ltr' == 'rtl' && 'start' == 'start')) {
   4130      text_align_dx = text_width;
   4131    }
   4132    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   4133    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   4134 
   4135    document.body.removeChild(el);
   4136 
   4137    return position.offset;
   4138  }
   4139 
   4140  ctx.font = '50px sans-serif';
   4141  ctx.direction = 'ltr';
   4142  ctx.textAlign = 'start';
   4143  ctx.letterSpacing = '10px';
   4144 
   4145  const kTexts = [
   4146    'UNAVAILABLE',
   4147    '🏁🎢🏁',
   4148    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   4149    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   4150    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   4151    '--abcd__'
   4152  ]
   4153 
   4154  for (text of kTexts) {
   4155    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   4156    const tm = ctx.measureText(text);
   4157    text_width = tm.width;
   4158    step = 30;
   4159    if ('10px' == '10px') {
   4160      step = 40;
   4161    }
   4162 
   4163    offset = step;
   4164    adjusted_offset = alignOffset(offset, text_width);
   4165    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4166    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4167    assert_equals(tm_position,
   4168                  doc_position,
   4169                  "for " + text + " offset " + offset);
   4170 
   4171    offset = text_width / 2 - 10;
   4172    adjusted_offset = alignOffset(offset, text_width);
   4173    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4174    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4175    assert_equals(tm_position,
   4176                  doc_position,
   4177                  "for " + text + " offset " + offset);
   4178 
   4179    offset = text_width / 2 + 10;
   4180    adjusted_offset = alignOffset(offset, text_width);
   4181    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4182    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4183    assert_equals(tm_position,
   4184                  doc_position,
   4185                  "for " + text + " offset " + offset);
   4186 
   4187    offset = text_width - step;
   4188    adjusted_offset = alignOffset(offset, text_width);
   4189    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4190    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4191    assert_equals(tm_position,
   4192                  doc_position,
   4193                  "for " + text + " offset " + offset);
   4194  }
   4195 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and directional-override.");
   4196 
   4197 test(t => {
   4198  const canvas = document.createElement('canvas');
   4199  canvas.width = 100;
   4200  canvas.height = 50;
   4201  const ctx = canvas.getContext('2d');
   4202 
   4203  function alignOffset(offset, width) {
   4204    if ('start' == 'center') {
   4205      offset -= width / 2;
   4206    } else if ('start' == 'right' ||
   4207             ('rtl' == 'ltr' && 'start' == 'end') ||
   4208             ('rtl' == 'rtl' && 'start' == 'start')) {
   4209      offset -= width;
   4210    }
   4211    return offset;
   4212  }
   4213 
   4214  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   4215    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   4216    const LTR_OVERRIDE = '\u202D';
   4217    const RTL_OVERRIDE = '\u202E';
   4218    const OVERRIDE_END = '\u202C';
   4219    if (direction_is_ltr) {
   4220      return LTR_OVERRIDE + text + OVERRIDE_END;
   4221    }
   4222    return RTL_OVERRIDE + text + OVERRIDE_END;
   4223  }
   4224 
   4225  function placeAndMeasureTextInDOM(text, text_width, point) {
   4226    const el = document.createElement("p");
   4227    el.innerHTML = text;
   4228    el.style.font = '50px sans-serif';
   4229    el.style.direction = 'rtl';
   4230    el.style.letterSpacing = '10px';
   4231    // Put the text top left to make offsets simpler.
   4232    el.style.padding = '0px';
   4233    el.style.margin = '0px';
   4234    el.style.position = 'absolute';
   4235    el.style.x = '0px';
   4236    el.style.y = '0px';
   4237    document.body.appendChild(el);
   4238    text_bound = el.getBoundingClientRect();
   4239    text_x = text_bound.x;
   4240    text_y = text_bound.y + text_bound.height / 2;
   4241 
   4242    // Offset to the requested point determined by textAlign and direction.
   4243    let text_align_dx = 0;
   4244    if ('start' == 'center') {
   4245      text_align_dx = text_width / 2;
   4246    } else if ('start' == 'right' ||
   4247             ('rtl' == 'ltr' && 'start' == 'end') ||
   4248             ('rtl' == 'rtl' && 'start' == 'start')) {
   4249      text_align_dx = text_width;
   4250    }
   4251    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   4252    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   4253 
   4254    document.body.removeChild(el);
   4255 
   4256    return position.offset;
   4257  }
   4258 
   4259  ctx.font = '50px sans-serif';
   4260  ctx.direction = 'rtl';
   4261  ctx.textAlign = 'start';
   4262  ctx.letterSpacing = '10px';
   4263 
   4264  const kTexts = [
   4265    'UNAVAILABLE',
   4266    '🏁🎢🏁',
   4267    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   4268    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   4269    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   4270    '--abcd__'
   4271  ]
   4272 
   4273  for (text of kTexts) {
   4274    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   4275    const tm = ctx.measureText(text);
   4276    text_width = tm.width;
   4277    step = 30;
   4278    if ('10px' == '10px') {
   4279      step = 40;
   4280    }
   4281 
   4282    offset = step;
   4283    adjusted_offset = alignOffset(offset, text_width);
   4284    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4285    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4286    assert_equals(tm_position,
   4287                  doc_position,
   4288                  "for " + text + " offset " + offset);
   4289 
   4290    offset = text_width / 2 - 10;
   4291    adjusted_offset = alignOffset(offset, text_width);
   4292    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4293    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4294    assert_equals(tm_position,
   4295                  doc_position,
   4296                  "for " + text + " offset " + offset);
   4297 
   4298    offset = text_width / 2 + 10;
   4299    adjusted_offset = alignOffset(offset, text_width);
   4300    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4301    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4302    assert_equals(tm_position,
   4303                  doc_position,
   4304                  "for " + text + " offset " + offset);
   4305 
   4306    offset = text_width - step;
   4307    adjusted_offset = alignOffset(offset, text_width);
   4308    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4309    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4310    assert_equals(tm_position,
   4311                  doc_position,
   4312                  "for " + text + " offset " + offset);
   4313  }
   4314 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and directional-override.");
   4315 
   4316 test(t => {
   4317  const canvas = document.createElement('canvas');
   4318  canvas.width = 100;
   4319  canvas.height = 50;
   4320  const ctx = canvas.getContext('2d');
   4321 
   4322  function alignOffset(offset, width) {
   4323    if ('end' == 'center') {
   4324      offset -= width / 2;
   4325    } else if ('end' == 'right' ||
   4326             ('ltr' == 'ltr' && 'end' == 'end') ||
   4327             ('ltr' == 'rtl' && 'end' == 'start')) {
   4328      offset -= width;
   4329    }
   4330    return offset;
   4331  }
   4332 
   4333  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   4334    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   4335    const LTR_OVERRIDE = '\u202D';
   4336    const RTL_OVERRIDE = '\u202E';
   4337    const OVERRIDE_END = '\u202C';
   4338    if (direction_is_ltr) {
   4339      return LTR_OVERRIDE + text + OVERRIDE_END;
   4340    }
   4341    return RTL_OVERRIDE + text + OVERRIDE_END;
   4342  }
   4343 
   4344  function placeAndMeasureTextInDOM(text, text_width, point) {
   4345    const el = document.createElement("p");
   4346    el.innerHTML = text;
   4347    el.style.font = '50px sans-serif';
   4348    el.style.direction = 'ltr';
   4349    el.style.letterSpacing = '10px';
   4350    // Put the text top left to make offsets simpler.
   4351    el.style.padding = '0px';
   4352    el.style.margin = '0px';
   4353    el.style.position = 'absolute';
   4354    el.style.x = '0px';
   4355    el.style.y = '0px';
   4356    document.body.appendChild(el);
   4357    text_bound = el.getBoundingClientRect();
   4358    text_x = text_bound.x;
   4359    text_y = text_bound.y + text_bound.height / 2;
   4360 
   4361    // Offset to the requested point determined by textAlign and direction.
   4362    let text_align_dx = 0;
   4363    if ('end' == 'center') {
   4364      text_align_dx = text_width / 2;
   4365    } else if ('end' == 'right' ||
   4366             ('ltr' == 'ltr' && 'end' == 'end') ||
   4367             ('ltr' == 'rtl' && 'end' == 'start')) {
   4368      text_align_dx = text_width;
   4369    }
   4370    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   4371    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   4372 
   4373    document.body.removeChild(el);
   4374 
   4375    return position.offset;
   4376  }
   4377 
   4378  ctx.font = '50px sans-serif';
   4379  ctx.direction = 'ltr';
   4380  ctx.textAlign = 'end';
   4381  ctx.letterSpacing = '10px';
   4382 
   4383  const kTexts = [
   4384    'UNAVAILABLE',
   4385    '🏁🎢🏁',
   4386    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   4387    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   4388    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   4389    '--abcd__'
   4390  ]
   4391 
   4392  for (text of kTexts) {
   4393    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   4394    const tm = ctx.measureText(text);
   4395    text_width = tm.width;
   4396    step = 30;
   4397    if ('10px' == '10px') {
   4398      step = 40;
   4399    }
   4400 
   4401    offset = step;
   4402    adjusted_offset = alignOffset(offset, text_width);
   4403    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4404    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4405    assert_equals(tm_position,
   4406                  doc_position,
   4407                  "for " + text + " offset " + offset);
   4408 
   4409    offset = text_width / 2 - 10;
   4410    adjusted_offset = alignOffset(offset, text_width);
   4411    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4412    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4413    assert_equals(tm_position,
   4414                  doc_position,
   4415                  "for " + text + " offset " + offset);
   4416 
   4417    offset = text_width / 2 + 10;
   4418    adjusted_offset = alignOffset(offset, text_width);
   4419    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4420    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4421    assert_equals(tm_position,
   4422                  doc_position,
   4423                  "for " + text + " offset " + offset);
   4424 
   4425    offset = text_width - step;
   4426    adjusted_offset = alignOffset(offset, text_width);
   4427    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4428    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4429    assert_equals(tm_position,
   4430                  doc_position,
   4431                  "for " + text + " offset " + offset);
   4432  }
   4433 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and directional-override.");
   4434 
   4435 test(t => {
   4436  const canvas = document.createElement('canvas');
   4437  canvas.width = 100;
   4438  canvas.height = 50;
   4439  const ctx = canvas.getContext('2d');
   4440 
   4441  function alignOffset(offset, width) {
   4442    if ('end' == 'center') {
   4443      offset -= width / 2;
   4444    } else if ('end' == 'right' ||
   4445             ('rtl' == 'ltr' && 'end' == 'end') ||
   4446             ('rtl' == 'rtl' && 'end' == 'start')) {
   4447      offset -= width;
   4448    }
   4449    return offset;
   4450  }
   4451 
   4452  function addDirectionalOverrideCharacters(text, direction_is_ltr) {
   4453    // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en
   4454    const LTR_OVERRIDE = '\u202D';
   4455    const RTL_OVERRIDE = '\u202E';
   4456    const OVERRIDE_END = '\u202C';
   4457    if (direction_is_ltr) {
   4458      return LTR_OVERRIDE + text + OVERRIDE_END;
   4459    }
   4460    return RTL_OVERRIDE + text + OVERRIDE_END;
   4461  }
   4462 
   4463  function placeAndMeasureTextInDOM(text, text_width, point) {
   4464    const el = document.createElement("p");
   4465    el.innerHTML = text;
   4466    el.style.font = '50px sans-serif';
   4467    el.style.direction = 'rtl';
   4468    el.style.letterSpacing = '10px';
   4469    // Put the text top left to make offsets simpler.
   4470    el.style.padding = '0px';
   4471    el.style.margin = '0px';
   4472    el.style.position = 'absolute';
   4473    el.style.x = '0px';
   4474    el.style.y = '0px';
   4475    document.body.appendChild(el);
   4476    text_bound = el.getBoundingClientRect();
   4477    text_x = text_bound.x;
   4478    text_y = text_bound.y + text_bound.height / 2;
   4479 
   4480    // Offset to the requested point determined by textAlign and direction.
   4481    let text_align_dx = 0;
   4482    if ('end' == 'center') {
   4483      text_align_dx = text_width / 2;
   4484    } else if ('end' == 'right' ||
   4485             ('rtl' == 'ltr' && 'end' == 'end') ||
   4486             ('rtl' == 'rtl' && 'end' == 'start')) {
   4487      text_align_dx = text_width;
   4488    }
   4489    position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y);
   4490    _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el");
   4491 
   4492    document.body.removeChild(el);
   4493 
   4494    return position.offset;
   4495  }
   4496 
   4497  ctx.font = '50px sans-serif';
   4498  ctx.direction = 'rtl';
   4499  ctx.textAlign = 'end';
   4500  ctx.letterSpacing = '10px';
   4501 
   4502  const kTexts = [
   4503    'UNAVAILABLE',
   4504    '🏁🎢🏁',
   4505    'οΌ‰οΌˆγ‚οΌ‰οΌˆ',
   4506    'οΌ‰οΌˆγ‚γ‚οΌ‰οΌˆ',
   4507    'γ‚γ‚οΌ‰οΌˆγ‚γ‚',
   4508    '--abcd__'
   4509  ]
   4510 
   4511  for (text of kTexts) {
   4512    text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr');
   4513    const tm = ctx.measureText(text);
   4514    text_width = tm.width;
   4515    step = 30;
   4516    if ('10px' == '10px') {
   4517      step = 40;
   4518    }
   4519 
   4520    offset = step;
   4521    adjusted_offset = alignOffset(offset, text_width);
   4522    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4523    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4524    assert_equals(tm_position,
   4525                  doc_position,
   4526                  "for " + text + " offset " + offset);
   4527 
   4528    offset = text_width / 2 - 10;
   4529    adjusted_offset = alignOffset(offset, text_width);
   4530    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4531    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4532    assert_equals(tm_position,
   4533                  doc_position,
   4534                  "for " + text + " offset " + offset);
   4535 
   4536    offset = text_width / 2 + 10;
   4537    adjusted_offset = alignOffset(offset, text_width);
   4538    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4539    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4540    assert_equals(tm_position,
   4541                  doc_position,
   4542                  "for " + text + " offset " + offset);
   4543 
   4544    offset = text_width - step;
   4545    adjusted_offset = alignOffset(offset, text_width);
   4546    tm_position = tm.getIndexFromOffset(adjusted_offset);
   4547    doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset);
   4548    assert_equals(tm_position,
   4549                  doc_position,
   4550                  "for " + text + " offset " + offset);
   4551  }
   4552 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and directional-override.");
   4553 
   4554 </script>