2011-04-20 Xiaomei Ji <xji@chromium.org>
[WebKit-https.git] / LayoutTests / editing / selection / move-by-word-visually.html
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5 <style>
6 div.test {
7     -webkit-user-modify: read-write;
8     padding: 4px;
9     border: 1px dashed lightblue;
10     margin: 4px 4px 4px 24px;
11     outline: none;
12     font-family: Lucida Grande;
13     counter-increment: test-number;
14 }
15 div.test:before { content: counter(test-number); position: absolute; left: 8px; font-size: x-small; text-align: right; width: 20px; }
16 div.test span { background-color: #def; }
17 div.test img { width: 1em; height: 1em; background-color: lightgreen; }
18 div.test img + img { background-color: lightblue; }
19 div.test div { border: 1px dashed pink; padding: 3px; height: 2em; }
20 test_move_by_word {display: none;}
21 </style>
22 <script>
23 var messages = [];
24
25 function log(message)
26 {
27     messages.push(message);
28 }
29
30 function flushLog()
31 {
32     document.getElementById("console").appendChild(document.createTextNode(messages.join("")));
33 }
34
35 function fold(string)
36 {
37     var result = "";
38     for (var i = 0; i < string.length; ++i) {
39         var char = string.charCodeAt(i);
40         if (char >= 0x05d0)
41             char -= 0x058f;
42         else if (char == 10) {
43             result += "\\n";
44             continue;
45         }
46         result += String.fromCharCode(char);
47     }
48     return result;
49 }
50
51 function logPositions(positions)
52 {
53     for (var i = 0; i < positions.length; ++i) {
54         if (i) {
55             if (positions[i].node != positions[i - 1].node)
56                 log("]");
57             log(", ");
58         }
59         if (!i || positions[i].node != positions[i - 1].node)
60             log((positions[i].node instanceof Text ? '"' + fold(positions[i].node.data) + '"' : "<" + positions[i].node.tagName + ">") + "[");
61         log(positions[i].offset);
62     }
63     log("]");
64 }
65
66 function nodeOfWordBreak(nodeAndOffset)
67 {
68     var node = document.getElementById(nodeAndOffset[0]).firstChild;
69     if (nodeAndOffset.length == 3) {
70         var childIndex = nodeAndOffset[2];
71         for (var i = 0; i < childIndex - 1; ++i) {
72             node = node.nextSibling;
73         }
74     }
75     return node;
76 }
77
78 var wordBreaks;
79
80 function logWordBreak(index)
81 {
82     var wordBreak = wordBreaks[index];
83     if (wordBreak.search(',') == -1)
84         log(wordBreak);
85     else {
86         var nodeAndOffset = wordBreak.split(',');
87         var node = nodeOfWordBreak(nodeAndOffset);
88
89         var differentNode = false;
90         if (index != 0) {
91             differentNode = nodeOfWordBreak(nodeAndOffset) != nodeOfWordBreak(wordBreaks[index - 1].split(','));
92         
93         }
94         if (differentNode == true)
95             log("]");
96         if (index == 0 || differentNode == true)
97             log((node instanceof Text ? '"' + fold(node.data) + '"' : "<" + node.tagName + ">") + "[");
98         log(nodeAndOffset[1]);
99     }
100 }
101
102 function positionEqualToWordBreak(position, wordBreak)
103 {
104     if (wordBreak.search(',') == -1)
105         return position.offset == wordBreak;
106     else {
107         var nodeAndOffset = wordBreak.split(',');
108         return position.node == nodeOfWordBreak(nodeAndOffset) && position.offset == nodeAndOffset[1];
109     }
110 }
111
112 function validateData(positions)
113 {
114     var equal = true;
115     if (positions.length != wordBreaks.length)
116         equal = false;
117     for (var i = 0; i < wordBreaks.length - 1; ++i) {
118         if (!positionEqualToWordBreak(positions[i], wordBreaks[i])) {
119             equal = false;
120             break;
121         }
122     }
123     if (equal == false) {
124         log("    FAIL expected: [");
125         for (var i = 0; i < wordBreaks.length; ++i) {
126             logWordBreak(i);
127             if (i != wordBreaks.length - 1)
128                 log(", ");
129         }
130         log("]");
131     }
132 }
133
134 function collectWordBreaks(test, searchDirection)
135 {
136     var title;
137     if (searchDirection == "right") 
138         title = test.title.split("|")[0];
139     else
140         title = test.title.split("|")[1];
141
142     var pattern = /\[(.+?)\]/g;
143     var result;
144     wordBreaks = [];
145     while ((result = pattern.exec(title)) != null) {
146         wordBreaks.push(result[1]);
147     }
148     if (wordBreaks.length == 0) {
149         wordBreaks = title.split(" ");
150     }
151 }
152
153 function setPosition(sel, node, wordBreak)
154 {
155     if (wordBreak.search(',') == -1)
156         sel.setPosition(node, wordBreak);
157     else {
158         var nodeAndOffset = wordBreak.split(',');
159         sel.setPosition(nodeOfWordBreak(nodeAndOffset), nodeAndOffset[1]);
160     }
161 }
162
163 function moveByWord(sel, test, searchDirection, dir)
164 {
165     log("Move " + searchDirection + " by one word\n");
166     var prevOffset = sel.anchorOffset;
167     var prevNode = sel.anchorNode;
168     var positions = [];
169     positions.push({ node: sel.anchorNode, offset: sel.anchorOffset });
170
171     while (1) {
172         sel.modify("move", searchDirection, "-webkit-visual-word");
173         if (prevNode == sel.anchorNode && prevOffset == sel.anchorOffset)
174             break;
175         positions.push({ node: sel.anchorNode, offset: sel.anchorOffset });
176         prevNode = sel.anchorNode;
177         prevOffset = sel.anchorOffset;
178     };
179     logPositions(positions);
180     collectWordBreaks(test, searchDirection);
181     validateData(positions);
182     log("\n");
183 }
184
185 function moveByWordForEveryPosition(sel, test, dir)
186 {
187     // Check ctrl-right-arrow works for every position.
188     sel.setPosition(test, 0);
189     var direction = "right";
190     if (dir == "rtl")
191         direction = "left";    
192     moveByWord(sel, test, direction, dir);    
193
194     sel.modify("move", "forward", "lineBoundary");
195     // Check ctrl-left-arrow works for every position.
196     if (dir == "ltr")
197         direction = "left";
198     else
199         direction = "right";    
200     moveByWord(sel, test, direction, dir);    
201 }
202
203 function runMoveLeftRight(tests, unit)
204 {
205     var sel = getSelection();
206     for (var i = 0; i < tests.length; ++i) {
207         var positionsMovingRight;
208         var positionsMovingLeft;
209
210         if (tests[i].getAttribute("dir") == 'ltr')
211         {
212             log("Test " + (i + 1) + ", LTR:\n");
213             moveByWordForEveryPosition(sel, tests[i], "ltr");
214         } else {
215             log("Test " + (i + 1) + ", RTL:\n");
216             moveByWordForEveryPosition(sel, tests[i], "rtl");
217         }
218
219     }
220 }
221
222 function runSpecificTest(tests)
223 {
224     var sel = getSelection();
225     log("\n======== Move By Word Specific Test ====\n");
226     for (var i = 0; i < tests.length; ++i) {
227         log("Test " + (i + 1) + "\n");
228         var components = tests[i].title.split(" ");
229         var startOffset = components[0];
230         var movingDirection = components[1];
231         var endingNode = components[2];
232         var endingOffset = components[3];
233  
234         log("Move " + movingDirection + " by one word\n");
235
236         var positions = [];
237         sel.setPosition(tests[i].firstChild, startOffset);
238         positions.push({ node: sel.anchorNode, offset: sel.anchorOffset });
239
240         sel.modify("move", movingDirection, "-webkit-visual-word");
241         positions.push({ node: sel.anchorNode, offset: sel.anchorOffset });
242
243         logPositions(positions);
244
245         if (sel.anchorNode.parentNode != document.getElementById(endingNode) || sel.anchorOffset != endingOffset) {
246             log("    FAIL: expected ");
247             var endingChildNode = document.getElementById(endingNode).firstChild;
248             log((endingChildNode instanceof Text ? '"' + fold(endingChildNode.data) + '"' : "<" + endingChildNode.tagName + ">") + "[" + endingOffset + "]");
249         }
250     }
251     document.getElementById("testMoveByWord").style.display = "none";
252 }
253
254 function runTest() {
255     log("\n======== Move By Word ====\n");
256     var tests = document.getElementsByClassName("test_move_by_word");
257     runMoveLeftRight(tests, "word");
258     
259     tests = document.getElementsByClassName("specific_test");
260     runSpecificTest(tests);
261 }
262
263 onload = function() {
264     try {
265         runTest();
266     } finally {
267         flushLog();
268     }
269 };
270
271 if (window.layoutTestController)
272     layoutTestController.dumpAsText();
273 </script>
274 </head>
275 <body>
276 <div id="testMoveByWord">
277 <!-- 
278 Title saves the word breaks.
279 The format of title is "xxx|xxxx".
280
281 The sequence on the left of "|" is word boundaries when moving caret from left to right.
282 The sequence on the right of "|" is word boundaries when moving caret from right to left.
283
284 If there is a single node in the line, the sequence are offsets.
285 If there are multiple nodes in the line, the sequence is array of [anchor_node_id, offset, child_node_index], 
286 where child_node_index is optional, default is the first child of the anchor node.
287 -->
288 <div dir=ltr class="test_move_by_word" title="0 4 8 12 16|19 16 12 8 4 0" contenteditable>abc def hij opq rst</div>
289
290 <!-- pure English -->
291 <div dir=ltr class="test_move_by_word" title="0 4 8 12 16|19 16 12 8 4 0" contenteditable>abc def hij opq rst</div>
292 <div dir=rtl class="test_move_by_word" title="19 3 7 11 15 0|0 15 11 7 3" contenteditable>abc def hij opq rst</div>
293
294 <!-- pure Hebrew -->
295 <div dir=ltr class="test_move_by_word" title="0 15 11 7 3|19 3 7 11 15 0" contenteditable>ששש נננ בבב גגג קקק</div>
296 <div dir=rtl class="test_move_by_word" title="19 16 12 8 4 0|0 4 8 12 16" contenteditable>ששש נננ בבב גגג קקק</div>
297
298 <!-- bidi text -->
299 <!-- English Hebrew English -->
300 <div dir=ltr class="test_move_by_word" title="0 4 8 12 19 15 24 28 32|35 32 28 24 15 19 12 8 4 0" contenteditable>abc def hij אאא בבב צצצ opr uvw xyz</div>
301 <div dir=rtl class="test_move_by_word" title="35 27 31 24 20 16 12 3 7 0|0 7 3 12 16 20 24 31 27" contenteditable>abc def hij אאא בבב צצצ opr uvw xyz</div>
302
303 <div dir=ltr class="test_move_by_word" title="0 4 8 11 16 20|23 20 16 11 8 4 0" contenteditable>abc def שנב סטז uvw xyz</div>
304 <div dir=rtl class="test_move_by_word" title="23 19 16 12 8 3 0|0 3 8 12 16 19" contenteditable>abc def שנב סטז uvw xyz</div>
305
306 <div dir=ltr class="test_move_by_word" title="0 4 8|11 8 4 0" contenteditable>aaa אאא bbb</div>
307 <div dir=rtl class="test_move_by_word" title="11 8 4 0|0 4 8 11" contenteditable>aaa אאא bbb</div>
308
309 <!-- Hebrew English Hebrew -->
310 <div dir=ltr class="test_move_by_word" title="0 7 3 12 16 20 24 31 27|35 27 31 24 20 16 12 3 7 0" contenteditable>אאא בבב צצצ aaa bbb ccc דדד עעע פפפ</div>
311 <div dir=rtl class="test_move_by_word" title="35 32 28 24 15 19 12 8 4 0|0 4 8 12 19 15 24 28 32" contenteditable>אאא בבב צצצ aaa bbb ccc דדד עעע פפפ</div>
312
313 <div dir=ltr class="test_move_by_word" title="0 3 8 12 16 19|23 19 16 12 8 3 0" contenteditable>אאא בבב aaa bbb צצצ דדד</div>
314 <div dir=rtl class="test_move_by_word" title="23 20 16 11 8 4 0|0 4 8 11 16 20" contenteditable>אאא בבב aaa bbb צצצ דדד</div>
315
316 <div dir=ltr class="test_move_by_word" title="0 4 8 11|11 8 4 0" contenteditable>שנב abc סטז</div>
317 <div dir=rtl class="test_move_by_word" title="11 8 4 0|0 4 8" contenteditable>שנב abc סטז</div>
318
319 <!-- multiple spaces between word. 
320      FAILED: word break between "def" and "hij" is unreachable.
321 -->
322 <div dir=ltr class="test_move_by_word" title="0 4 11 15|18 15 11 4 0" contenteditable>abc def    hij opq</div>
323
324 <!-- Inline element -->
325 <div dir=ltr id="div_1" class="test_move_by_word" title="[div_1, 0][div_1, 3]|[span_1, 2][div_1, 3][div_1,0]" contenteditable>אאא <span id="span_1">בב</span></div>
326 <div dir=rtl id="div_2" class="test_move_by_word" title="[span_2, 2][div_2, 4][div_2, 0]|[div_2, 0][div_2, 4]" contenteditable>אאא <span id="span_2">בב</span></div>
327
328 <!-- pure English in inline element with same or different directionality from its parent -->
329 <div dir=ltr id="div_3" class="test_move_by_word" title="[div_3, 0][div_3, 4][div_3, 8][span_3, 4][div_3, 1, 3][div_3, 5, 3]|[div_3, 8, 3][div_3, 5, 3][div_3, 1, 3][span_3, 4][div_3, 8][div_3, 4][div_3, 0]" contenteditable>abc def <span id="span_3">hij opq</span> rst uvw</div>
330
331 <!-- FAILED -->
332 <div dir=rtl id="div_4" class="test_move_by_word" title="[div_4, 8, 3][div_4, 3, 1][div_4, 7, 1][span_4, 3, 1][div_4, 0, 3][div_4, 4, 3][div_4, 0, 1]|[div_4, 0, 1][div_4, 4, 3][div_4, 0, 3][span_4, 3, 1][div_4, 7, 1][div_4, 3, 1]" contenteditable>abc def <span id="span_4">hij opq</span> rst uvw</div>
333
334 <!-- FAILED -->
335 <div id="div_5" dir=rtl class="test_move_by_word" title="[div_5, 8, 3][div_5, 3, 1][div_5, 7, 1][span_5, 3, 1][div_5, 0, 3][div_5, 4, 3][div_5, 0, 1]|[div_5, 0, 1][div_5, 4, 3][div_5, 0, 3][span_5, 3, 1][div_5, 7, 1][div_5, 3, 1]"contenteditable>abc def <span dir=ltr id="span_5">hij opq</span> rst uvw</div>
336
337 <div id="div_6" dir=ltr class="test_move_by_word" title="[div_6, 0, 1][div_6, 4, 1][div_6, 8, 1][span_6, 4, 1][div_6, 1, 3][div_6, 5, 3]|[div_6, 8, 3][div_6, 5, 3][div_6, 1, 3][span_6, 4, 1][div_6, 8, 1][div_6, 4, 1][div_6, 0, 1]" contenteditable>abc def <span dir=rtl id="span_6">hij opq</span> rst uvw</div>
338
339 <!-- pure Hebrew in inline element with same or different directionality from its parent -->
340
341 <div id="div_7" dir=rtl class="test_move_by_word" title="[div_7, 7, 3][div_7, 4, 3][span_7, 4, 1][div_7, 8, 1][div_7, 4, 1][div_7, 0, 1]|[div_7, 0, 1][div_7, 4, 1][div_7, 8, 1][span_7, 4, 1][div_7, 4, 3]" contenteditable>אבד דעפ <span dir=ltr id="span_7">היח ופק</span>ווש כטז</div>
342
343 <div id="div_8" dir=ltr class="test_move_by_word" title="[div_8, 0, 1][div_8, 3, 3][span_8, 3, 1][div_8, 7, 1][div_8, 3, 1]|[div_8, 7, 3][div_8, 3, 1][div_8, 7, 1][span_8, 3, 1][div_8, 3, 3][div_8, 0, 1]" contenteditable>אבד דעפ <span dir=rtl id="span_8">היח ופק</span>ווש כטז</div>
344
345 <div id="div_9" dir=rtl class="test_move_by_word" title="[div_9, 7, 3][div_9, 4, 3][span_9, 4, 1][div_9, 8, 1][div_9, 4, 1][div_9, 0, 1]|[div_9, 0, 1][div_9, 4, 1][div_9, 8, 1][span_9, 4, 1][div_9, 4, 3]" contenteditable>אבד דעפ <span id="span_9">היח ופק</span>ווש כטז</div>
346
347 <div id="div_10" dir=ltr class="test_move_by_word" title="[div_10, 0, 1][div_10, 3, 3][span_10, 3, 1][div_10, 7, 1][div_10, 3, 1]|[div_10, 7, 3][div_10, 3, 1][div_10, 7, 1][span_10, 3, 1][div_10, 3, 3][div_10, 0, 1]" contenteditable>אבד דעפ <span id="span_10">היח ופק</span>ווש כטז</div>
348
349 <!-- bidi in inline element with same or different directionality from its parent -->
350 <div id="div_11" dir=rtl class="test_move_by_word" title="[div_11, 7, 3][div_11, 4, 3][span_11, 3, 1][div_11, 8, 1][div_11, 4, 1][div_11, 0, 1]|[div_11, 0, 1][div_11, 4, 1][div_11, 8, 1][span_11, 3, 1][div_11, 4, 3]" contenteditable>אבד דעפ <span dir=ltr id="span_11">abc def</span>ווש כטז</div>
351
352 <!-- FAIL -->
353 <div id="div_12" dir=ltr class="test_move_by_word" title="[div_12, 0, 1][div_12, 3, 3][div_12, 8, 1][span_12, 4, 1][div_12, 7, 1][div_12, 3, 1]|[div_12, 7, 3][div_12, 3, 1][div_12, 7, 1][span_12, 4, 1][div_12, 8, 1][div_12, 3, 3][div_12, 0]" contenteditable>אבד דעפ <span dir=rtl id="span_12">abc def</span>ווש כטז</div>
354
355 <div id="div_13" dir=rtl class="test_move_by_word" title="[div_13, 7, 3][div_13, 4, 3][span_13, 3, 1][div_13, 8, 1][div_13, 4, 1][div_13, 0, 1]|[div_13, 0, 1][div_13, 4, 1][div_13, 8, 1][span_13, 3, 1][div_13, 4, 3]" contenteditable>אבד דעפ <span id="span_13">abc def</span>ווש כטז</div>
356
357 <div id="div_14" dir=ltr class="test_move_by_word" title="[div_14, 0, 1][div_14, 3, 1][div_14, 8, 1][span_14, 4, 1][div_14, 3, 3]|[div_14, 7, 3][div_14, 3, 3][span_14, 4, 1][div_14, 8, 1][div_14, 4, 1][div_14, 0, 1]" contenteditable>אבד דעפ <span id="span_14">abc def</span>ווש כטז</div>
358
359 <!-- FAILED -->
360 <div id="div_15" dir=rtl class="test_move_by_word" title="[div_15, 11, 3][div_15, 8, 3][div_15, 4, 3][span_15, 3, 1][span_15, 4, 1][div_15, 12, 1][div_15, 8, 1][div_15, 4, 1][div_15, 0, 1]|[div_15, 0, 1][div_15, 4, 1][div_15, 8, 1][div_15, 12, 1][span_15, 4, 1][span_15, 3, 1][div_15, 4, 3][div_15, 8, 3]"
361 contenteditable>אבד opq דעפ <span dir=ltr id="span_15">abc אאא def</span>ווש rst כטז</div>
362
363 <!-- FAILED, and wrong printing result -->
364 <div id="div_16" dir=ltr class="test_move_by_word" title="[div_16, 0, 1][div_16, 4, 1][div_16, 8, 1][span_16, 8, 1][span_16, 7, 1][div_16, 12, 1][div_16, 11, 1][div_16, 4, 3][div_16, 8, 3][div_16, 11, 3]|[div_16, 11, 3][div_16, 8, 3][div_16, 4, 3][div_16, 11, 1][div_16, 12, 1][span_16, 7, 1][span_16, 8, 1][div_16, 8, 1][div_16, 4, 1][div_16, 0, 1]" contenteditable>אבד opq דעפ <span dir=rtl id="span_16">abc אאא def</span>ווש rst כטז</div>
365
366 <!-- FAILED -->
367 <div id="div_17" dir=rtl class="test_move_by_word" title="[div_17, 11, 3][div_17, 8, 3][div_17, 4, 3][span_17, 8, 1][span_17, 4, 1][div_17, 12, 1][div_17, 8, 1][div_17, 4, 1][div_17, 0, 1]|[div_17, 0, 1][div_17, 4, 1][div_17, 8, 1][div_17, 12, 1][span_17, 4, 1][span_17, 8, 1][div_17, 4, 3][div_17, 8, 3]" contenteditable>אבד opq דעפ <span id="span_17">abc אאא def</span>ווש rst כטז</div>
368
369 <div id="div_18" dir=ltr class="test_move_by_word" title="[div_18, 0, 1][div_18, 4, 1][div_18, 8, 1][div_18, 12, 1][span_18, 4, 1][span_18, 8, 1][div_18, 4, 3][div_18, 8, 3][div_18, 11, 3]|[div_18, 11, 3][div_18, 8, 3][div_18, 4, 3][span_18, 8, 1][span_18, 4, 1][div_18, 12, 1][div_18, 8, 1][div_18, 4, 1][div_18, 0, 1]" contenteditable>אבד opq דעפ <span id="span_18">abc אאא def</span>ווש rst כטז</div>
370
371 <div id="div_19" dir=ltr class="test_move_by_word" title="[div_19, 0, 1][div_19, 4, 1][span_19, 4, 1][span_19, 7, 1]|[div_19, 3, 3][span_19, 7, 1][span_19, 4, 1][div_19, 4, 1][div_19, 0, 1]" contenteditable>aaa <span id="span_19">bbb אאא </span>ווש</div>
372
373 <!-- The content in title means "startOffsetInCurrentNode movingDirectionByWord endingNode endingOffsetInEndingNode" -->
374 <div id="div_100" contenteditable>אאא <span id="span_100" class="specific_test" title="1 left div_100 0">בב</span></div>
375 </div>
376
377 <pre id="console"></pre>
378 </body>
379 </html>