summaryrefslogtreecommitdiffstats
path: root/layout/generic/test/test_backspace_delete.xhtml
blob: 2e914bc7274a037bea1d4b7387049e01301758ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        title="Test BackSpace/Delete Keys">
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>

<script class="testbody" type="application/javascript">
<![CDATA[

function execTests() {
  var e = document.getElementById("edit");
  var doc = e.contentDocument;
  var win = e.contentWindow;
  var root = doc.documentElement;
  var editor = doc.body;
  var sel = win.getSelection();
  win.focus();

  function setupTest(html, firstChildOffsetForCaret, node) {
    // Work around bug 474255 --- we need to have nonempty content before we turn on
    // editing, or the tests below break because the editor doesn't notice when we
    // insert non-empty content using innerHTML.
    doc.designMode = 'off';
    editor.innerHTML = html;
    doc.designMode = 'on';
    var n = editor.firstChild;
    if (node) {
      n = node();
    }
    sel.collapse(n, firstChildOffsetForCaret);
  }

  var eatSpace;
  var deleteImmediately;

  function getPrefs(branch) {
    const prefSvcContractID = "@mozilla.org/preferences-service;1";
    const prefSvcIID = Ci.nsIPrefService;
    return Cc[prefSvcContractID].getService(prefSvcIID)
             .getBranch(branch);
  }

  function setPref(branch, pref, newValue) {
    getPrefs(branch).setBoolPref(pref, newValue);
    return newValue;
  }

  function restorePref(branch, pref, newValue) {
    try {
      getPrefs(branch).clearUserPref(pref);
    } catch(ex) {}
  }

  function setEatSpace(newValue) {
    eatSpace = setPref("layout.word_select.", "eat_space_to_next_word", newValue);
  }

  function restoreEatSpace() {
    restorePref("layout.word_select.", "eat_space_to_next_word");
  }

  function setDeleteImmediately(newValue) {
    deleteImmediately = setPref("bidi.edit.", "delete_immediately", newValue);
  }

  function restoreDeleteImmediately() {
    restorePref("bidi.edit.", "delete_immediately");
  }

  function doCommand(cmd) {
    var controller = document.commandDispatcher.getControllerForCommand(cmd);
    if (controller) {
      try {
        controller.doCommand(cmd);
        ok(true, 'doCommand(' + cmd + ') succeeded');
      } catch(ex) {
        ok(false, 'exception in doCommand(' + cmd + '): ', ex.message);
      }
    }
  }

  function testRight(node, offset) {
    doCommand("cmd_charNext");
    var msg = "Right movement broken in \"" + editor.innerHTML + "\", offset " + offset;
    is(sel.anchorNode, node, msg);
    is(sel.anchorOffset, offset, msg);
  }

  function selErrString(dir) {
    return dir + " selection broken with eatSpace=" + eatSpace + " in \"" + editor.innerHTML + "\"";
  }

  function testWordSelRight(startNode, startOffset, endNode, endOffset) {
    doCommand("cmd_selectWordNext");
    var selRange = sel.getRangeAt(0);
    is(selRange.startContainer, startNode, selErrString("Word right"));
    is(selRange.startOffset, startOffset, selErrString("Word right"));
    is(selRange.endContainer, endNode, selErrString("Word right"));
    is(selRange.endOffset, endOffset, selErrString("Word right"));
  }

  function testDelete(node, offset, text, richtext) {
    doCommand("cmd_deleteCharForward");
    var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
    if(typeof node == 'function'){
      node = node();
    }
    is(sel.anchorNode, node, msg);

    is(sel.anchorOffset, offset, msg);
    let text_result = richtext ? editor.innerHTML : editor.textContent;
    is(text_result, text, msg);
  }

  function testBackspace(node, offset, text) {
    doCommand("cmd_deleteCharBackward");
    var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
    is(sel.anchorNode, node, msg);

    is(sel.anchorOffset, offset, msg);
    is(editor.textContent, text, msg);
  }

  function testDeletePrevWord(node, offset, text) {
    doCommand("cmd_deleteWordBackward");
    var msg = "Delete previous word broken in \"" + editor.innerHTML + "\", offset " + offset;
    is(sel.anchorNode, node, msg);
    is(sel.anchorOffset, offset, msg);
    is(editor.textContent, text, msg);
  }

  function testDeleteNextWord(node, offset, text) {
    doCommand("cmd_deleteWordForward");
    var msg = "Delete next word broken in \"" + editor.innerHTML + "\", offset " + offset;
    is(sel.anchorNode, node, msg);
    is(sel.anchorOffset, offset, msg);
    todo_is(editor.textContent, text, msg);
  }

  // Test cell-wise deletion of Delete
  setupTest("สวัสดีพ่อแม่พี่น้อง", 0);
  testRight(editor.firstChild, 1);
  testDelete(editor.firstChild, 1, "สสดีพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 2);
  testDelete(editor.firstChild, 2, "สสพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 4);
  testDelete(editor.firstChild, 4, "สสพ่แม่พี่น้อง");
  testRight(editor.firstChild, 5);
  testDelete(editor.firstChild, 5, "สสพ่แพี่น้อง", false);
  testRight(editor.firstChild, 8);
  testDelete(editor.firstChild, 8, "สสพ่แพี่อง", false);
  testRight(editor.firstChild, 9);
  testDelete(editor.firstChild, 9, "สสพ่แพี่อ", false);

  // Test character-wise deletion of Backspace
  setupTest("สวัสดีพ่อแม่พี่น้อง", 0);
  testRight(editor.firstChild, 1);
  testBackspace(editor.firstChild, 0, "วัสดีพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 2);
  testBackspace(editor.firstChild, 1, "วสดีพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 2);
  testBackspace(editor.firstChild, 1, "วดีพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 3);
  testBackspace(editor.firstChild, 2, "วดพ่อแม่พี่น้อง");
  testRight(editor.firstChild, 4);
  testBackspace(editor.firstChild, 3, "วดพอแม่พี่น้อง");
  testRight(editor.firstChild, 4);
  testBackspace(editor.firstChild, 3, "วดพแม่พี่น้อง");
  testRight(editor.firstChild, 4);
  testBackspace(editor.firstChild, 3, "วดพม่พี่น้อง");
  testRight(editor.firstChild, 5);
  testBackspace(editor.firstChild, 4, "วดพมพี่น้อง");
  testRight(editor.firstChild, 7);
  testBackspace(editor.firstChild, 6, "วดพมพีน้อง");
  testRight(editor.firstChild, 8);
  testBackspace(editor.firstChild, 7, "วดพมพีนอง");
  testRight(editor.firstChild, 8);
  testBackspace(editor.firstChild, 7, "วดพมพีนง");
  testRight(editor.firstChild, 8);
  testBackspace(editor.firstChild, 7, "วดพมพีน");

  // Tests for Bug 417745

  setEatSpace(true);

  setupTest("Quick yellow fox", 0);
  testWordSelRight(editor.firstChild, 0, editor.firstChild, 6);
  testDelete(editor.firstChild, 0, "yellow fox");
  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
  testDelete(editor.firstChild, 0, "fox");

  setEatSpace(false);

  setupTest("Quick yellow fox", 0);
  testWordSelRight(editor.firstChild, 0, editor.firstChild, 5);
  // editor converts the leading space to an &nbsp;, otherwise it
  // wouldn't show up which would confuse users
  testDelete(editor.firstChild, 0, "\u00A0yellow fox");
  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
  testDelete(editor.firstChild, 0, "\u00A0fox");
  testWordSelRight(editor.firstChild, 0, editor.firstChild, 4);
  testDelete(editor, 0, "");

  restoreEatSpace();

  // Tests for Bug 419217

  setupTest("foo<div>bar</div>", 3);
  testDelete(function(){return editor.firstChild;}, 3, "foobar", true);

  // Tests for Bug 419406
  var s = "helloשלום";

  setDeleteImmediately(true);

  setupTest(s, 4);
  testRight(editor.firstChild, 5);
  testDelete(editor.firstChild, 5, "helloלום");

  setDeleteImmediately(false);

  setupTest(s, 4);
  testRight(editor.firstChild, 5);
  testDelete(editor.firstChild, 5, "helloשלום");

  // Tests for bug 1034337
  s = "اهلاhello";

  setDeleteImmediately(true);

  setupTest(s, 4);
  // first delete an ltr character to make sure that the caret is ltr
  testDelete(editor.firstChild, 4, "اهلاello");
  testBackspace(editor.firstChild, 3, "اهلello");

  setDeleteImmediately(false);

  setupTest(s, 4);
  // first delete an ltr character to make sure that the caret is ltr
  testDelete(editor.firstChild, 4, "اهلاello");
  testBackspace(editor.firstChild, 4, "اهلاello");

  restoreDeleteImmediately();

  // Tests for Bug 462188
  setupTest("You should not see this text.", 29);
  testDeletePrevWord(editor.firstChild, 24, "You should not see this ");
  testDeletePrevWord(editor.firstChild, 19, "You should not see ");
  testDeletePrevWord(editor.firstChild, 15, "You should not ");
  testDeletePrevWord(editor.firstChild, 11, "You should ");
  testDeletePrevWord(editor.firstChild,  4, "You ");
  testDeletePrevWord(editor,  0, "");

  setupTest("You should not see this text.", 0);
  testDeleteNextWord(editor.firstChild, 0, "\u00A0should not see this text.");
  testDeleteNextWord(editor.firstChild, 0, "\u00A0not see this text.");
  testDeleteNextWord(editor.firstChild, 0, "\u00A0see this text.");
  testDeleteNextWord(editor.firstChild, 0, "\u00A0this text.");
  testDeleteNextWord(editor.firstChild, 0, "\u00A0text.");
  // testDeleteNextWord(editor, 0, "");

  // Tests for Bug 502259
  setupTest("<p>Bug</p>\n<p>502259</p>", 1);
  testDelete(function(){return editor.firstChild.firstChild;}, 3, "<p>Bug502259</p>", true);

  // Tests for Bug 507936
  var nodecallback = function(){return editor.firstChild.firstChild.lastChild.firstChild.lastChild;};
  setupTest("<ol><li>one<ol><li>two</li></ol></li></ol>\n<p>three</p>", 3, nodecallback);
  testDelete(nodecallback, 0, "<ol><li>one<ol><li>twothree</li></ol></li></ol>", true);

  setupTest("<ol><li>one<ol><li>two</li></ol></li></ol>\n<hr>\n<p>three</p>", 3, nodecallback);
  testDelete(nodecallback, 3, 
    "<ol><li>one<ol><li>two</li></ol></li></ol><p>three</p>", true);

  // Tests for Bug 519751
  var nodecallback = function(){return editor.firstChild.lastChild;};
  setupTest("<p>one</p><ol><li>two</li><li>three</li></ol>", 3, nodecallback);
  testDelete(nodecallback, 0, "<p>onetwo</p><ol><li>three</li></ol>", true);

  nodecallback = function(){return editor.firstChild.childNodes[1].firstChild;};
  setupTest("<ol><li>one</li><li>two</li></ol><ol><li>three</li><li>four</li></ol>", 3, nodecallback);
  testDelete(function(){return editor.firstChild.childNodes[2].firstChild;}, 
    0, "<ol><li>one</li><li>two</li><li>three</li><li>four</li></ol>", true);
  /*todo_is(false, true, 'The above testDelete should use the same nodecallback' +
    'as in the proceeding setupTest: the cursor should stay at the end of "two", while currently it is at the beginning of "three" after delete');*/

  // More Tests for Bug 507936
  nodecallback = function(){return editor.firstChild.firstChild.firstChild;}
  setupTest("<div><div>abcdef</div><div>bar</div><div>ghi</div></div>", 5, nodecallback);
  sel.extend(editor.lastChild.lastChild.lastChild, 1);
  testDelete(editor.lastChild.lastChild.lastChild, 5, "<div><div>abcdehi</div></div>", true);

  setupTest("<div><div>abcdef</div><div>ghi</div></div>", 5, nodecallback);
  sel.extend(editor.lastChild.lastChild.lastChild, 1);
  testDelete(editor.lastChild.lastChild.lastChild, 5, "<div><div>abcdehi</div></div>", true);

  nodecallback = function(){return editor.firstChild.firstChild;}
  setupTest("<div>abcdef<div><div>bar</div>ghi</div></div>", 5, nodecallback);
  sel.extend(editor.lastChild.lastChild.lastChild, 1);
  expectednodecallback = function(){return editor.lastChild.lastChild;}
  testDelete(expectednodecallback, 0, "<div>abcdehi</div>", true);

  setupTest("<div>abcdef<div>ghi</div></div>", 5, nodecallback);
  sel.extend(editor.lastChild.lastChild.lastChild, 1);
  testDelete(expectednodecallback, 0, "<div>abcdehi</div>", true);

  SimpleTest.finish();
}

SimpleTest.waitForExplicitFinish();
addLoadEvent(execTests);
]]>
</script>

<body  id="html_body" xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462188">Mozilla Bug 462188</a>
<p id="display"></p>

<pre id="test">
</pre>
<iframe id="edit" width="200" height="100" src="about:blank"/>
</body>
</window>