chromium/third_party/blink/web_tests/editing/selection/modify_move/resources/move-by-word-visually.js

function nodeOfWordBreak(nodeAndOffset)
{
    var node = document.getElementById(nodeAndOffset[0]).firstChild;
    if (nodeAndOffset.length == 3) {
        var childIndex = nodeAndOffset[2];
        for (var i = 0; i < childIndex - 1; ++i) {
            node = node.nextSibling;
        }
    }
    return node;
}

function positionEqualToWordBreak(position, wordBreak)
{
    if (wordBreak.search(',') == -1)
        return position.offset == wordBreak;
    else {
        var nodeAndOffset = wordBreak.split(',');
        return position.node == nodeOfWordBreak(nodeAndOffset) && position.offset == nodeAndOffset[1];
    }
}

function validateData(positions, wordBreaks)
{
    assert_equals(positions.length, wordBreaks.length, 'wordBreaks.length');
    for (var i = 0; i < wordBreaks.length - 1; ++i)
        assert_true(positionEqualToWordBreak(positions[i], wordBreaks[i]), 'positionEqualToWordBreak of ' + i);
}

const TEST_FOR_CRASH = 'test_for_crash';

function collectWordBreaks(test, searchDirection)
{
    if (test.title == TEST_FOR_CRASH)
        return TEST_FOR_CRASH;

    var title;
    if (searchDirection == 'right')
        title = test.title.split('|')[0];
    else
        title = test.title.split('|')[1];

    var pattern = /\[(.+?)\]/g;
    var result;
    var wordBreaks = [];
    while ((result = pattern.exec(title)) != null) {
        wordBreaks.push(result[1]);
    }
    if (wordBreaks.length == 0) {
        wordBreaks = title.split(' ');
    }
    return wordBreaks;
}

function moveByWord(sel, test, searchDirection, dir)
{
    var prevOffset = internals.visibleSelectionAnchorOffset;
    var prevNode = internals.visibleSelectionAnchorNode;
    var positions = [];
    positions.push({ node: prevNode, offset: prevOffset });

    while (1) {
        sel.modify('move', searchDirection, 'word');
        if (prevNode == internals.visibleSelectionAnchorNode && prevOffset == internals.visibleSelectionAnchorOffset)
            break;
        prevNode = internals.visibleSelectionAnchorNode;
        prevOffset = internals.visibleSelectionAnchorOffset;
        positions.push({ node: prevNode, offset: prevOffset });
    };

    var wordBreaks = collectWordBreaks(test, searchDirection);
    if (wordBreaks == TEST_FOR_CRASH)
        return;
    validateData(positions, wordBreaks);
}

function moveByWordOnEveryChar(sel, test, searchDirection, dir)
{
    var wordBreaks = collectWordBreaks(test, searchDirection);
    var wordBreakIndex = 1;
    var prevOffset = internals.visibleSelectionAnchorOffset;
    var prevNode = internals.visibleSelectionAnchorNode;

    while (1) {
        sel.modify('move', searchDirection, 'word');

        if (wordBreaks != TEST_FOR_CRASH) {
            if (wordBreakIndex >= wordBreaks.length) {
                assert_equals(internals.visibleSelectionAnchorNode, prevNode, 'expected to stay in the same position');
                assert_equals(internals.visibleSelectionAnchorOffset, prevOffset, 'expected to stay in the same position');
            } else {
                assert_true(positionEqualToWordBreak({ node: internals.visibleSelectionAnchorNode, offset: internals.visibleSelectionAnchorOffset }, wordBreaks[wordBreakIndex]), 'positionEqualToWordBreak of ' + wordBreakIndex);
            }
        }

        // Restore position and move by 1 character.
        sel.collapse(prevNode, prevOffset);
        sel.modify('move', searchDirection, 'character');
        if (prevNode == internals.visibleSelectionAnchorNode && prevOffset == internals.visibleSelectionAnchorOffset)
            return;

        prevNode = internals.visibleSelectionAnchorNode;
        prevOffset = internals.visibleSelectionAnchorOffset;
        if (wordBreakIndex < wordBreaks.length &&
            positionEqualToWordBreak({ node: prevNode, offset: prevOffset }, wordBreaks[wordBreakIndex])) {
            ++wordBreakIndex;
        }
    };
}

function moveByWordForEveryPosition(sel, test, dir)
{
    // Check ctrl-right-arrow works for every position.
    sel.collapse(test, 0);
    var direction = 'right';
    if (dir == 'rtl')
        direction = 'left';
    moveByWord(sel, test, direction, dir);
    sel.collapse(test, 0);
    moveByWordOnEveryChar(sel, test, direction, dir);

    sel.modify('move', 'forward', 'lineBoundary');
    var position = { node: internals.visibleSelectionAnchorNode, offset: internals.visibleSelectionAnchorOffset };

    // Check ctrl-left-arrow works for every position.
    if (dir == 'ltr')
        direction = 'left';
    else
        direction = 'right';
    moveByWord(sel, test, direction, dir);

    sel.collapse(position.node, position.offset);
    moveByWordOnEveryChar(sel, test, direction, dir);
}

function runTest() {
  div = document.createElement('div');
  div.id = 'log';
  document.body.appendChild(div);

  var tests = document.getElementsByClassName('test_move_by_word');
  var sel = getSelection();
  for (var testcase of tests) {
      test(function () {
          assert_own_property(window, 'internals');
          if (testcase.className.search('fix_width') != -1) {
              var span = document.getElementById('span_size');
              var length = span.offsetWidth;
              testcase.style.width = length + 'px';
          }

          if (testcase.getAttribute('dir') == 'ltr')
              moveByWordForEveryPosition(sel, testcase, 'ltr');
          else
              moveByWordForEveryPosition(sel, testcase, 'rtl');
      }, testcase.id);
  }
}