Tue, 06 Jan 2015 21:39:09 +0100
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>