tor-browser

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

test_atcaretoffset.html (14576B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4  <title>Test: nsIAccessibleText getText* functions at caret offset</title>
      5 
      6  <link rel="stylesheet" type="text/css"
      7        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
      8 
      9  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     10  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
     11 
     12  <script type="application/javascript"
     13          src="../common.js"></script>
     14  <script type="application/javascript"
     15          src="../role.js"></script>
     16  <script type="application/javascript"
     17          src="../states.js"></script>
     18  <script type="application/javascript"
     19          src="../events.js"></script>
     20  <script type="application/javascript"
     21          src="../text.js"></script>
     22 
     23  <script type="application/javascript">
     24    // gA11yEventDumpToConsole = true; // debugging
     25 
     26    function traverseTextByLines(aQueue, aID, aLines) {
     27      var wholeText = "";
     28      for (var i = 0; i < aLines.length ; i++)
     29        wholeText += aLines[i][0] + aLines[i][1];
     30 
     31      var baseInvokerFunc = synthClick;
     32      var charIter = new charIterator(wholeText, aLines);
     33      // charIter.debugOffset = 10; // enable to run tests at given offset only
     34 
     35      while (charIter.next()) {
     36        aQueue.push(new tmpl_moveTo(aID, baseInvokerFunc, wholeText, charIter));
     37        baseInvokerFunc = synthRightKey;
     38      }
     39    }
     40 
     41    /**
     42     * Used to get test list for each traversed character.
     43     */
     44    function charIterator(aWholeText, aLines) {
     45      this.next = function charIterator_next() {
     46        // Don't increment offset if we are at end of the wrapped line
     47        // (offset is shared between end of this line and start of next line).
     48        if (this.mAtWrappedLineEnd) {
     49          this.mAtWrappedLineEnd = false;
     50          this.mLine = this.mLine.nextLine;
     51          return true;
     52        }
     53 
     54        this.mOffset++;
     55        if (this.mOffset > aWholeText.length)
     56          return false;
     57 
     58        var nextLine = this.mLine.nextLine;
     59        if (!nextLine.isFakeLine() && this.mOffset == nextLine.start) {
     60          if (nextLine.start == this.mLine.end)
     61            this.mAtWrappedLineEnd = true;
     62          else
     63            this.mLine = nextLine;
     64        }
     65 
     66        return true;
     67      };
     68 
     69      Object.defineProperty(this, "offset", { get() { return this.mOffset; },
     70      });
     71 
     72      Object.defineProperty(this, "offsetDescr", { get() {
     73          return this.mOffset + " offset (" + this.mLine.number + " line, " +
     74            (this.mOffset - this.mLine.start) + " offset on the line)";
     75        },
     76      });
     77 
     78      Object.defineProperty(this, "tests", { get() {
     79          // Line boundary tests.
     80          var cLine = this.mLine;
     81          var pLine = cLine.prevLine;
     82          var ppLine = pLine.prevLine;
     83          var nLine = cLine.nextLine;
     84          var nnLine = nLine.nextLine;
     85 
     86          var lineTests = [
     87            [ testTextBeforeOffset, BOUNDARY_LINE_START, pLine.start, cLine.start],
     88            [ testTextBeforeOffset, BOUNDARY_LINE_END, ppLine.end, pLine.end],
     89            [ testTextAtOffset, BOUNDARY_LINE_START, cLine.start, nLine.start],
     90            [ testTextAtOffset, BOUNDARY_LINE_END, pLine.end, cLine.end],
     91            [ testTextAfterOffset, BOUNDARY_LINE_START, nLine.start, nnLine.start],
     92            [ testTextAfterOffset, BOUNDARY_LINE_END, cLine.end, nLine.end],
     93          ];
     94 
     95          // Word boundary tests.
     96          var cWord = this.mLine.firstWord;
     97          var nWord = cWord.nextWord, pWord = cWord.prevWord;
     98 
     99          // The current word is a farthest word starting at or after the offset.
    100          if (this.mOffset >= nWord.start) {
    101            while (this.mOffset >= nWord.start && !this.mLine.isLastWord(cWord)) {
    102              cWord = nWord;
    103              nWord = nWord.nextWord;
    104            }
    105            pWord = cWord.prevWord;
    106          } else if (this.mOffset < cWord.start) {
    107            while (this.mOffset < cWord.start) {
    108              cWord = pWord;
    109              pWord = pWord.prevWord;
    110            }
    111            nWord = cWord.nextWord;
    112          }
    113 
    114          var nnWord = nWord.nextWord, ppWord = pWord.prevWord;
    115 
    116          var isAfterWordEnd =
    117            this.mOffset > cWord.end || cWord.line != this.mLine;
    118          var isAtOrAfterWordEnd = (this.mOffset >= cWord.end);
    119          var useNextWordForAtWordEnd =
    120            isAtOrAfterWordEnd && this.mOffset != aWholeText.length;
    121 
    122          var wordTests = [
    123            [ testTextBeforeOffset, BOUNDARY_WORD_START,
    124              pWord.start, cWord.start ],
    125            [ testTextBeforeOffset, BOUNDARY_WORD_END,
    126              (isAfterWordEnd ? pWord : ppWord).end,
    127              (isAfterWordEnd ? cWord : pWord).end ],
    128            [ testTextAtOffset, BOUNDARY_WORD_START,
    129              cWord.start, nWord.start ],
    130            [ testTextAtOffset, BOUNDARY_WORD_END,
    131              (useNextWordForAtWordEnd ? cWord : pWord).end,
    132              (useNextWordForAtWordEnd ? nWord : cWord).end ],
    133            [ testTextAfterOffset, BOUNDARY_WORD_START,
    134              nWord.start, nnWord.start ],
    135            [ testTextAfterOffset, BOUNDARY_WORD_END,
    136              (isAfterWordEnd ? nWord : cWord).end,
    137              (isAfterWordEnd ? nnWord : nWord).end ],
    138          ];
    139 
    140          // Character boundary tests.
    141          var prevOffset = this.offset > 1 ? this.offset - 1 : 0;
    142          var nextOffset = this.offset >= aWholeText.length ?
    143            this.offset : this.offset + 1;
    144          var nextAfterNextOffset = nextOffset >= aWholeText.length ?
    145            nextOffset : nextOffset + 1;
    146 
    147          var charTests = [
    148            [ testTextBeforeOffset, BOUNDARY_CHAR,
    149              prevOffset, this.offset ],
    150            [ testTextAtOffset, BOUNDARY_CHAR,
    151              this.offset,
    152              this.mAtWrappedLineEnd ? this.offset : nextOffset ],
    153            [ testTextAfterOffset, BOUNDARY_CHAR,
    154              this.mAtWrappedLineEnd ? this.offset : nextOffset,
    155              this.mAtWrappedLineEnd ? nextOffset : nextAfterNextOffset ],
    156          ];
    157 
    158          return lineTests.concat(wordTests.concat(charTests));
    159        },
    160      });
    161 
    162      Object.defineProperty(this, "failures", { get() {
    163          if (this.mOffset == this.mLine.start)
    164            return this.mLine.lineStartFailures;
    165          if (this.mOffset == this.mLine.end)
    166            return this.mLine.lineEndFailures;
    167          return [];
    168        },
    169      });
    170 
    171      this.mOffset = -1;
    172      this.mLine = new line(aWholeText, aLines, 0);
    173      this.mAtWrappedLineEnd = false;
    174      this.mWord = this.mLine.firstWord;
    175    }
    176 
    177    /**
    178     * A line object. Allows to navigate by lines and by words.
    179     */
    180    function line(aWholeText, aLines, aIndex) {
    181      Object.defineProperty(this, "prevLine", { get() {
    182          return new line(aWholeText, aLines, aIndex - 1);
    183        },
    184      });
    185      Object.defineProperty(this, "nextLine", { get() {
    186          return new line(aWholeText, aLines, aIndex + 1);
    187        },
    188      });
    189 
    190      Object.defineProperty(this, "start", { get() {
    191          if (aIndex < 0)
    192            return 0;
    193 
    194          if (aIndex >= aLines.length)
    195            return aWholeText.length;
    196 
    197          return aLines[aIndex][2];
    198        },
    199      });
    200      Object.defineProperty(this, "end", { get() {
    201          if (aIndex < 0)
    202            return 0;
    203 
    204          if (aIndex >= aLines.length)
    205            return aWholeText.length;
    206 
    207          return aLines[aIndex][3];
    208        },
    209      });
    210 
    211      Object.defineProperty(this, "number", { get() { return aIndex; },
    212      });
    213      Object.defineProperty(this, "wholeText", { get() { return aWholeText; },
    214      });
    215      this.isFakeLine = function line_isFakeLine() {
    216        return aIndex < 0 || aIndex >= aLines.length;
    217      };
    218 
    219      Object.defineProperty(this, "lastWord", { get() {
    220          if (aIndex < 0)
    221            return new word(this, [], -1);
    222          if (aIndex >= aLines.length)
    223            return new word(this, [], 0);
    224 
    225          var words = aLines[aIndex][4].words;
    226          return new word(this, words, words.length - 2);
    227        },
    228      });
    229      Object.defineProperty(this, "firstWord", { get() {
    230          if (aIndex < 0)
    231            return new word(this, [], -1);
    232          if (aIndex >= aLines.length)
    233            return new word(this, [], 0);
    234 
    235          var words = aLines[aIndex][4].words;
    236          return new word(this, words, 0);
    237        },
    238      });
    239 
    240      this.isLastWord = function line_isLastWord(aWord) {
    241        var lastWord = this.lastWord;
    242        return lastWord.start == aWord.start && lastWord.end == aWord.end;
    243      };
    244 
    245      Object.defineProperty(this, "lineStartFailures", { get() {
    246          if (aIndex < 0 || aIndex >= aLines.length)
    247            return [];
    248 
    249          return aLines[aIndex][4].lsf || [];
    250        },
    251      });
    252      Object.defineProperty(this, "lineEndFailures", { get() {
    253          if (aIndex < 0 || aIndex >= aLines.length)
    254            return [];
    255 
    256          return aLines[aIndex][4].lef || [];
    257        },
    258      });
    259    }
    260 
    261    /**
    262     * A word object. Allows to navigate by words.
    263     */
    264    function word(aLine, aWords, aIndex) {
    265      Object.defineProperty(this, "prevWord", { get() {
    266          if (aIndex >= 2)
    267            return new word(aLine, aWords, aIndex - 2);
    268 
    269          var prevLineLastWord = aLine.prevLine.lastWord;
    270          if (this.start == prevLineLastWord.start && !this.isFakeStartWord())
    271            return prevLineLastWord.prevWord;
    272          return prevLineLastWord;
    273        },
    274      });
    275      Object.defineProperty(this, "nextWord", { get() {
    276          if (aIndex + 2 < aWords.length)
    277            return new word(aLine, aWords, aIndex + 2);
    278 
    279          var nextLineFirstWord = aLine.nextLine.firstWord;
    280          if (this.end == nextLineFirstWord.end && !this.isFakeEndWord())
    281            return nextLineFirstWord.nextWord;
    282          return nextLineFirstWord;
    283        },
    284      });
    285 
    286      Object.defineProperty(this, "line", { get() { return aLine; } });
    287 
    288      Object.defineProperty(this, "start", { get() {
    289          if (this.isFakeStartWord())
    290            return 0;
    291 
    292          if (this.isFakeEndWord())
    293            return aLine.end;
    294           return aWords[aIndex];
    295        },
    296      });
    297      Object.defineProperty(this, "end", { get() {
    298          if (this.isFakeStartWord())
    299            return 0;
    300 
    301          return this.isFakeEndWord() ? aLine.end : aWords[aIndex + 1];
    302        },
    303      });
    304 
    305      this.toString = function word_toString() {
    306        var start = this.start, end = this.end;
    307        return "'" + aLine.wholeText.substring(start, end) +
    308          "' at [" + start + ", " + end + "]";
    309      };
    310 
    311      this.isFakeStartWord = function() { return aIndex < 0; };
    312      this.isFakeEndWord = function() { return aIndex >= aWords.length; };
    313    }
    314 
    315    /**
    316     * A template invoker to move through the text.
    317     */
    318    function tmpl_moveTo(aID, aInvokerFunc, aWholeText, aCharIter) {
    319      this.offset = aCharIter.offset;
    320 
    321      var checker = new caretMoveChecker(this.offset, true, aID);
    322      this.__proto__ = new (aInvokerFunc)(aID, checker);
    323 
    324      this.finalCheck = function genericMoveTo_finalCheck() {
    325        if (this.noTests())
    326          return;
    327 
    328        for (var i = 0; i < this.tests.length; i++) {
    329          var func = this.tests[i][0];
    330          var boundary = this.tests[i][1];
    331          var startOffset = this.tests[i][2];
    332          var endOffset = this.tests[i][3];
    333          var text = aWholeText.substring(startOffset, endOffset);
    334 
    335          var isOk1 = kOk, isOk2 = kOk, isOk3 = kOk;
    336          for (var fIdx = 0; fIdx < this.failures.length; fIdx++) {
    337            var failure = this.failures[fIdx];
    338            if (func.name.includes(failure[0]) && boundary == failure[1]) {
    339              isOk1 = failure[2];
    340              isOk2 = failure[3];
    341              isOk3 = failure[4];
    342            }
    343          }
    344 
    345          func(kCaretOffset, boundary, text, startOffset, endOffset,
    346               aID, isOk1, isOk2, isOk3);
    347        }
    348      };
    349 
    350      this.getID = function genericMoveTo_getID() {
    351        return "move to " + this.offsetDescr;
    352      };
    353 
    354      this.noTests = function tmpl_moveTo_noTests() {
    355        return ("debugOffset" in aCharIter) &&
    356          (aCharIter.debugOffset != this.offset);
    357      };
    358 
    359      this.offsetDescr = aCharIter.offsetDescr;
    360      this.tests = this.noTests() ? null : aCharIter.tests;
    361      this.failures = aCharIter.failures;
    362    }
    363 
    364    var gQueue = null;
    365    function doTest() {
    366      gQueue = new eventQueue();
    367 
    368      // __a__w__o__r__d__\n
    369      //  0  1  2  3  4  5
    370      // __t__w__o__ (soft line break)
    371      //  6  7  8  9
    372      // __w__o__r__d__s
    373      // 10 11 12 13 14 15
    374 
    375      traverseTextByLines(gQueue, "textarea",
    376                          [ [ "aword", "\n", 0, 5, { words: [ 0, 5 ] } ],
    377                            [ "two ", "", 6, 10, { words: [ 6, 9 ] } ],
    378                            [ "words", "", 10, 15, { words: [ 10, 15 ] } ],
    379                          ] );
    380 
    381      var line4 = [ // "riend "
    382        [ "TextBeforeOffset", BOUNDARY_WORD_END,
    383          kOk, kOk, kOk],
    384        [ "TextAfterOffset", BOUNDARY_WORD_END,
    385          kOk, kOk, kOk ],
    386      ];
    387      traverseTextByLines(gQueue, "ta_wrapped",
    388                          [ [ "hi ", "", 0, 3, { words: [ 0, 2 ] } ],
    389                            [ "hello ", "", 3, 9, { words: [ 3, 8 ] } ],
    390                            [ "my ", "", 9, 12, { words: [ 9, 11 ] } ],
    391                            [ "longf", "", 12, 17, { words: [ 12, 17 ] } ],
    392                            [ "riend ", "", 17, 23, { words: [ 17, 22 ], lsf: line4 } ],
    393                            [ "t sq ", "", 23, 28, { words: [ 23, 24, 25, 27 ] } ],
    394                            [ "t", "", 28, 29, { words: [ 28, 29 ] } ],
    395                          ] );
    396 
    397      gQueue.invoke(); // will call SimpleTest.finish();
    398    }
    399 
    400    SimpleTest.waitForExplicitFinish();
    401    addA11yLoadEvent(doTest);
    402  </script>
    403 </head>
    404 <body>
    405 
    406  <a target="_blank"
    407     title="nsIAccessibleText getText related functions tests at caret offset"
    408     href="https://bugzilla.mozilla.org/show_bug.cgi?id=852021">
    409   Bug 852021
    410  </a>
    411  <p id="display"></p>
    412  <div id="content" style="display: none"></div>
    413  <pre id="test">
    414 
    415  <textarea id="textarea" cols="5">aword
    416 two words</textarea>
    417 
    418  <!-- scrollbar-width: none is needed so that the width of the scrollbar
    419    doesn't incorrectly affect the width of the textarea on some systems.
    420    See bug 1600170 and bug 33654.
    421    -->
    422  <textarea id="ta_wrapped" cols="5" style="scrollbar-width: none;">hi hello my longfriend t sq t</textarea>
    423  </pre>
    424 </body>
    425 </html>