accessible/tests/mochitest/text/test_atcaretoffset.html

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial