summaryrefslogtreecommitdiffstats
path: root/dom/events/test/test_moving_and_expanding_selection_per_page.html
blob: cc4786235d5f271e3ac3dfe9cdd8f0265ce00d58 (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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
<!DOCTYPE html>
<html>
<head>
  <title>Test for expanding selection per page</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>

<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(() => {
  open("window_empty_document.html", "_blank", "width=500,height=500");
});

async function doTests(aWindow) {
  // On macOS, there is no shortcut keys to extend selection per page.
  // Therefore, we need to use nsISelectionController.pageMove() instead.
  const kUseKeyboardEvent = !navigator.platform.includes("Mac");
  let selectionController = SpecialPowers.wrap(aWindow)
                                         .docShell
                                         .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                                         .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
                                         .QueryInterface(SpecialPowers.Ci.nsISelectionController);
  // On Windows, per-page selection to start or end expands selection to same
  // column of first or last line.  On the other platforms, it expands selection
  // to start or end of first or last line.
  const kSelectToStartOrEnd = !navigator.platform.includes("Win");

  await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]});
  await SimpleTest.promiseFocus(aWindow);

  function getNodeDescription(aNode) {
    function getElementDescription(aElement) {
      if (aElement.getAttribute("id") !== null) {
        return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`;
      }
      if (aElement.tagName === "BR") {
        return `${getElementDescription(aElement.previousSibling)} + br`;
      }
      return aElement.tagName.toLowerCase();
    }
    switch (aNode.nodeType) {
      case aNode.TEXT_NODE:
        return `text node in ${getElementDescription(aNode.parentElement)}`;
      case aNode.ELEMENT_NODE:
        return getElementDescription(aNode);
      default:
        return "unknown node";
    }
  }

  function doTest(aExpandSelection) {
    // Note that when neither editor has focus nor in caret mode, key navigation
    // does not call nsISelectionController::PageMove().  Therefore, in such
    // cases, you need to call doPageDown() and doPageUp() with setting true
    // to aUseSelectionController.
    function doPageDown(aUseSelectionController) {
      if (kUseKeyboardEvent && !aUseSelectionController) {
        synthesizeKey("KEY_PageDown", {shiftKey: aExpandSelection}, aWindow);
      } else {
        selectionController.pageMove(true, aExpandSelection);
      }
    }

    function doPageUp(aUseSelectionController) {
      if (kUseKeyboardEvent && !aUseSelectionController) {
        synthesizeKey("KEY_PageUp", {shiftKey: aExpandSelection}, aWindow);
      } else {
        selectionController.pageMove(false, aExpandSelection);
      }
    }

    let doc = aWindow.document;
    let body = doc.body;
    let selection = doc.getSelection();
    let container;

    body.innerHTML = '<span id="s1">first line</span><br>' +
                     '<span id="s2">second line</span><br>' +
                     '<span id="s3">last line</span>';
    container = doc.documentElement;

    let description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable body: `;
    is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
    selection.collapse(doc.getElementById("s1").firstChild, 3);
    doPageDown(!aExpandSelection);
    is(container.scrollTop, 0, description + "this test shouldn't create scrollable document");
    let range = selection.getRangeAt(0);
    if (aExpandSelection) {
      is(range.startContainer, doc.getElementById("s1").firstChild,
         `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
      is(range.startOffset, 3,
         `${description} selection should be expanded from the first line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
    is(range.endContainer, doc.getElementById("s3").firstChild,
       `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.endOffset, range.endContainer.length,
         `${description} selection should be expanded to end of the last line`);
    } else {
      isfuzzy(range.endOffset, 3, 2,
              `${description} selection should be expanded to around the last line's 3rd insertion point`);
    }

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable body: `;
    selection.collapse(doc.getElementById("s3").firstChild, 3);
    doPageUp(!aExpandSelection);
    is(container.scrollTop, 0, description + "this test shouldn't create scrollable document");
    range = selection.getRangeAt(0);
    is(range.startContainer, doc.getElementById("s1").firstChild,
       `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.startOffset, 0,
         `${description} selection should be expanded to start of the first line`);
    } else {
      isfuzzy(range.startOffset, 3, 2,
              `${description} selection should be expanded to around the first line's 3rd insertion point`);
    }
    if (aExpandSelection) {
      is(range.endContainer, doc.getElementById("s3").firstChild,
         `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
      is(range.endOffset, 3,
         `${description} selection should be expanded from the last line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }

    body.innerHTML = '<span id="s1">first line in the body</span>' +
                       '<div id="d1" style="height: 2em; line-height: 1em; overflow: auto;">' +
                         '<span id="s2">first line</span><br>' +
                         '<span id="s3">second line</span><br>' +
                         '<span id="s4">third line</span><br>' +
                         '<span id="s5">last line</span>' +
                       "</div>" +
                     '<span id="s6">last line in the body</span>';
    container = doc.getElementById("d1");

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable area in the body: `;
    is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
    selection.collapse(doc.getElementById("s2").firstChild, 3);
    doPageDown(!aExpandSelection);
    isnot(container.scrollTop, 0, description + "should be scrolled down");
    range = selection.getRangeAt(0);
    if (aExpandSelection) {
      is(range.startContainer, doc.getElementById("s2").firstChild,
         `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
      is(range.startOffset, 3,
         `${description} selection should be expanded from the first line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
    is(range.endContainer, doc.getElementById("s4").firstChild,
       `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
    isfuzzy(range.endOffset, 3, 2,
            `${description} selection should be expanded to around the 3rd line's 3rd insertion point`);

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable area in the body: `;
    selection.collapse(doc.getElementById("s4").firstChild, 3);
    let previousScrollTop = container.scrollTop;
    doPageUp(!aExpandSelection);
    ok(container.scrollTop < previousScrollTop, description + "should be scrolled up");
    range = selection.getRangeAt(0);
    is(range.startContainer, doc.getElementById("s2").firstChild,
       `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
    isfuzzy(range.startOffset, 3, 2,
            `${description} selection should be expanded to around the first line's 3rd insertion point`);
    if (aExpandSelection) {
      is(range.endContainer, doc.getElementById("s4").firstChild,
         `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
      is(range.endOffset, 3,
         `${description} selection should be expanded from the 3rd line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }

    body.innerHTML = '<span id="s1">first line in the body</span>' +
                       '<div id="d1" contenteditable style="height: 2em; line-height: 1em; overflow: auto;">' +
                         '<span id="s2">first line</span><br>' +
                         '<span id="s3">second line</span><br>' +
                         '<span id="s4">third line</span><br>' +
                         '<span id="s5">last line</span>' +
                       "</div>" +
                     '<span id="s6">last line in the body</span>';
    container = doc.getElementById("d1");

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable editable div in the body: `;
    is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
    selection.collapse(doc.getElementById("s2").firstChild, 3);
    doPageDown();
    isnot(container.scrollTop, 0, description + "should be scrolled down");
    range = selection.getRangeAt(0);
    if (aExpandSelection) {
      is(range.startContainer, doc.getElementById("s2").firstChild,
         `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
      is(range.startOffset, 3,
         `${description} selection should be expanded from the first line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
    is(range.endContainer, doc.getElementById("s4").firstChild,
       `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
    isfuzzy(range.endOffset, 3, 2,
            `${description} selection should be expanded to around the 3rd line's 3rd insertion point`);

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable editable div in the body: `;
    selection.collapse(doc.getElementById("s4").firstChild, 3);
    previousScrollTop = container.scrollTop;
    doPageUp();
    ok(container.scrollTop < previousScrollTop, description + "should be scrolled up");
    range = selection.getRangeAt(0);
    is(range.startContainer, doc.getElementById("s2").firstChild,
       `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
    isfuzzy(range.startOffset, 3, 2,
            `${description} selection should be expanded to around the first line's 3rd insertion point`);
    if (aExpandSelection) {
      is(range.endContainer, doc.getElementById("s4").firstChild,
         `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
      is(range.endOffset, 3,
         `${description} selection should be expanded from the 3rd line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }

    body.innerHTML = '<span id="s1">first line in the body</span>' +
                       '<div id="d1" contenteditable>' +
                         '<span id="s2">first line</span><br>' +
                         '<span id="s3">second line</span><br>' +
                         '<span id="s4">third line</span><br>' +
                         '<span id="s5">last line</span>' +
                       "</div>" +
                     '<span id="s6">last line in the body</span>';
    container = doc.getElementById("d1");

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable editable div in the body: `;
    is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
    selection.collapse(doc.getElementById("s2").firstChild, 3);
    doPageDown();
    is(container.scrollTop, 0, description + "editable div shouldn't be scrollable");
    range = selection.getRangeAt(0);
    if (aExpandSelection) {
      is(range.startContainer, doc.getElementById("s2").firstChild,
         `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
      is(range.startOffset, 3,
         `${description} selection should be expanded from the first line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
    is(range.endContainer, doc.getElementById("s5").firstChild,
       `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.endOffset, range.endContainer.length,
         `${description} selection should be expanded to end of the last line`);
    } else {
      isfuzzy(range.endOffset, 3, 2,
              `${description} selection should be expanded to around the last line's 3rd insertion point`);
    }

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable editable div in the body: `;
    selection.collapse(doc.getElementById("s5").firstChild, 3);
    doPageUp();
    is(container.scrollTop, 0, description + "editable div shouldn't be scrollable");
    range = selection.getRangeAt(0);
    is(range.startContainer, doc.getElementById("s2").firstChild,
       `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.startOffset, 0,
         `${description} selection should be expanded to start of the first line`);
    } else {
      isfuzzy(range.startOffset, 3, 2,
              `${description} selection should be expanded to around the first line's 3rd insertion point`);
    }
    if (aExpandSelection) {
      is(range.endContainer, doc.getElementById("s5").firstChild,
         `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
      is(range.endOffset, 3,
         `${description} selection should be expanded from the last line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }

    body.innerHTML = '<span id="s1">first line in the body</span>' +
                       '<div id="d1" contenteditable>' +
                         '<span id="s2">first editable line</span><br>' +
                         '<div id="d2" style="height: 3em; line-height: 1em; overflow: auto;">' +
                           '<span id="s3">first line</span><br>' +
                           '<span id="s4">second line</span>' +
                         "</div>" +
                         '<span id="s5">last editable line</span>' +
                       "</div>" +
                     '<span id="s6">last line in the body</span>';
    container = doc.getElementById("d2");

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable div (but not scrollable along y-axis) in the editable div: `;
    is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
    selection.collapse(doc.getElementById("s3").firstChild, 3);
    doPageDown();
    is(container.scrollTop, 0, description + "scrollable div in the editable div (but not scrollable along y-axis) shouldn't be scrollable");
    range = selection.getRangeAt(0);
    if (aExpandSelection) {
      is(range.startContainer, doc.getElementById("s3").firstChild,
         `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
      is(range.startOffset, 3,
         `${description} selection should be expanded from the first line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
    is(range.endContainer, doc.getElementById("s5").firstChild,
       `${description} selection should be expanded into the last editable line (got: ${getNodeDescription(range.endContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.endOffset, range.endContainer.length,
         `${description} selection should be expanded to end of the last editable line`);
    } else {
      isfuzzy(range.endOffset, 3, 2,
              `${description} selection should be expanded to around the last editable line's 3rd insertion point`);
    }

    description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable div (but not scrollable along y-axis) in the editable div: `;
    selection.collapse(doc.getElementById("s4").firstChild, 3);
    doPageUp();
    is(container.scrollTop, 0, description + "scrollable div (but not scrollable along y-axis) in the editable div shouldn't be scrollable");
    range = selection.getRangeAt(0);
    is(range.startContainer, doc.getElementById("s2").firstChild,
       `${description} selection should be expanded into the first editable line (got: ${getNodeDescription(range.startContainer)})`);
    if (kSelectToStartOrEnd) {
      is(range.startOffset, 0,
         `${description} selection should be expanded to start of the first editable line`);
    } else {
      isfuzzy(range.startOffset, 3, 2,
              `${description} selection should be expanded to around the first editable line's 3rd insertion point`);
    }
    if (aExpandSelection) {
      is(range.endContainer, doc.getElementById("s4").firstChild,
         `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
      is(range.endOffset, 3,
         `${description} selection should be expanded from the last line's 3rd insertion point`);
    } else {
      ok(range.collapsed, `${description} selection should be collapsed`);
    }
  }

  doTest(false);
  doTest(true);

  aWindow.close();
  SimpleTest.finish();
}
</script>
</html>