Bug 1874684 - Part 17: Fix uninitialised variable warnings from clang-tidy. r=allstarschh
[gecko.git] / editor / libeditor / tests / test_resizers_resizing_elements.html
blobf30b6bb269b42055d2b35a727d04a60b5d3f03e7
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <title>Test for resizers of some elements</title>
5 <script src="/tests/SimpleTest/SimpleTest.js"></script>
6 <script src="/tests/SimpleTest/EventUtils.js"></script>
7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
8 <style>
9 #target {
10 background-color: green;
12 </style>
13 </head>
14 <body>
15 <p id="display"></p>
16 <div id="content" contenteditable style="width: 200px; height: 200px;"></div>
17 <div id="clickaway" style="width: 10px; height: 10px"></div>
18 <img src="green.png"><!-- for ensuring to load the image at first test of <img> case -->
19 <pre id="test">
20 <script type="application/javascript">
21 "use strict";
23 SimpleTest.waitForExplicitFinish();
24 SimpleTest.waitForFocus(async () => {
25 document.execCommand("enableObjectResizing", false, true);
26 ok(document.queryCommandState("enableObjectResizing"),
27 "Object resizer should be enabled by the call of execCommand");
28 // Disable inline-table-editing UI for this test.
29 document.execCommand("enableInlineTableEditing", false, false);
31 let outOfEditor = document.getElementById("clickaway");
33 function cancel(e) { e.stopPropagation(); }
34 let content = document.getElementById("content");
35 content.addEventListener("mousedown", cancel);
36 content.addEventListener("mousemove", cancel);
37 content.addEventListener("mouseup", cancel);
39 async function waitForSelectionChange() {
40 return new Promise(resolve => {
41 document.addEventListener("selectionchange", () => {
42 resolve();
43 }, {once: true});
44 });
47 async function doTest(aDescription, aPreserveRatio, aInnerHTML) {
48 let description = aDescription;
49 if (document.queryCommandState("enableAbsolutePositionEditing")) {
50 description += " (absolute position editor is enabled)";
52 description += ": ";
53 content.innerHTML = aInnerHTML;
54 let target = document.getElementById("target");
56 /**
57 * This function is a generic resizer test.
58 * We have 8 resizers that we'd like to test, and each can be moved in 8 different directions.
59 * In specifying baseX, W can be considered to be the width of the image, and for baseY, H
60 * can be considered to be the height of the image. deltaX and deltaY are regular pixel values
61 * which can be positive or negative.
62 * TODO: Should test canceling "beforeinput" events case.
64 const W = 1;
65 const H = 1;
66 async function testResizer(baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY) {
67 ok(true, description + "testResizer(" + [baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY].join(", ") + ")");
69 // Reset the dimensions of the target.
70 target.style.width = "150px";
71 target.style.height = "150px";
72 let rect = target.getBoundingClientRect();
73 is(rect.width, 150, description + "Sanity check the width");
74 is(rect.height, 150, description + "Sanity check the height");
76 // Click on the target to show the resizers
77 ok(true, "waiting selectionchange to select the target element");
78 let promiseSelectionChangeEvent = waitForSelectionChange();
79 synthesizeMouseAtCenter(target, {});
80 await promiseSelectionChangeEvent;
82 // Determine which resizer we're dealing with.
83 let basePosX = rect.width * baseX;
84 let basePosY = rect.height * baseY;
86 let inputEventExpected = true;
87 function onInput(aEvent) {
88 if (!inputEventExpected) {
89 ok(false, `"${aEvent.type}" event shouldn't be fired after stopping resizing`);
90 return;
92 ok(aEvent instanceof InputEvent,
93 `"${aEvent.type}" event should be dispatched with InputEvent interface`);
94 is(aEvent.cancelable, false,
95 `"${aEvent.type}" event should be never cancelable`);
96 is(aEvent.bubbles, true,
97 `"${aEvent.type}" event should always bubble`);
98 is(aEvent.inputType, "",
99 `inputType of "${aEvent.type}" event should be empty string when an element is resized`);
100 is(aEvent.data, null,
101 `data of "${aEvent.type}" event should be null ${aDescription}`);
102 is(aEvent.dataTransfer, null,
103 `data of "${aEvent.type}" event should be null ${aDescription}`);
104 let targetRanges = aEvent.getTargetRanges();
105 if (aEvent.type === "beforeinput") {
106 let selection = document.getSelection();
107 is(targetRanges.length, selection.rangeCount,
108 `getTargetRanges() of "beforeinput" event for position changing of absolute position should return selection ranges ${aDescription}`);
109 if (targetRanges.length === selection.rangeCount) {
110 for (let i = 0; i < selection.rangeCount; i++) {
111 let range = selection.getRangeAt(i);
112 is(targetRanges[i].startContainer, range.startContainer,
113 `startContainer of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match ${aDescription}`);
114 is(targetRanges[i].startOffset, range.startOffset,
115 `startOffset of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match ${aDescription}`);
116 is(targetRanges[i].endContainer, range.endContainer,
117 `endContainer of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match ${aDescription}`);
118 is(targetRanges[i].endOffset, range.endOffset,
119 `endOffset of getTargetRanges()[${i}] of "beforeinput" event for position changing of absolute position does not match ${aDescription}`);
122 } else {
123 is(targetRanges.length, 0,
124 `getTargetRanges() of "${aEvent.type}" event for position changing of absolute position should return empty array ${aDescription}`);
128 content.addEventListener("beforeinput", onInput);
129 content.addEventListener("input", onInput);
131 // Click on the correct resizer
132 synthesizeMouse(target, basePosX, basePosY, {type: "mousedown"});
133 // Drag it delta pixels to the right and bottom (or maybe left and top!)
134 synthesizeMouse(target, basePosX + deltaX, basePosY + deltaY, {type: "mousemove"});
135 // Release the mouse button
136 synthesizeMouse(target, basePosX + deltaX, basePosY + deltaY, {type: "mouseup"});
138 inputEventExpected = false;
140 // Move the mouse delta more pixels to the same direction to make sure that the
141 // resize operation has stopped.
142 synthesizeMouse(target, basePosX + deltaX * 2, basePosY + deltaY * 2, {type: "mousemove"});
144 // Click outside of the editor to hide the resizers
145 ok(true, "waiting selectionchange to select outside the target element");
146 let promiseSelectionExitEvent = waitForSelectionChange();
147 synthesizeMouseAtCenter(outOfEditor, {});
148 await promiseSelectionExitEvent;
150 // Get the new dimensions for the target
151 // XXX I don't know why we need 2px margin to check this on Android.
152 // Fortunately, this test checks whether objects are resizable
153 // actually. So, bigger difference is okay.
154 let newRect = target.getBoundingClientRect();
155 isfuzzy(newRect.width, rect.width + expectedDeltaX, 2, description + "The width should be increased by " + expectedDeltaX + " pixels");
156 isfuzzy(newRect.height, rect.height + expectedDeltaY, 2, description + "The height should be increased by " + expectedDeltaY + "pixels");
158 content.removeEventListener("beforeinput", onInput);
159 content.removeEventListener("input", onInput);
162 // Account for changes in the resizing behavior when we're trying to preserve
163 // the aspect ration of image.
164 // ignoredGrowth means we don't change the size of a dimension because otherwise
165 // the aspect ratio would change undesirably.
166 // needlessGrowth means that we change the size of a dimension perpendecular to
167 // the mouse movement axis in order to preserve the aspect ratio.
168 // reversedGrowth means that we change the size of a dimension in the opposite
169 // direction to the mouse movement in order to maintain the aspect ratio.
170 const ignoredGrowth = aPreserveRatio ? 0 : 1;
171 const needlessGrowth = aPreserveRatio ? 1 : 0;
172 const reversedGrowth = aPreserveRatio ? -1 : 1;
174 /* eslint-disable no-multi-spaces */
176 // top resizer
177 await testResizer(W / 2, 0, -10, -10, 0, 10);
178 await testResizer(W / 2, 0, -10, 0, 0, 0);
179 await testResizer(W / 2, 0, -10, 10, 0, -10);
180 await testResizer(W / 2, 0, 0, -10, 0, 10);
181 await testResizer(W / 2, 0, 0, 0, 0, 0);
182 await testResizer(W / 2, 0, 0, 10, 0, -10);
183 await testResizer(W / 2, 0, 10, -10, 0, 10);
184 await testResizer(W / 2, 0, 10, 0, 0, 0);
185 await testResizer(W / 2, 0, 10, 10, 0, -10);
187 // top right resizer
188 await testResizer( W, 0, -10, -10, -10 * reversedGrowth, 10);
189 await testResizer( W, 0, -10, 0, -10 * ignoredGrowth, 0);
190 await testResizer( W, 0, -10, 10, -10, -10);
191 await testResizer( W, 0, 0, -10, 10 * needlessGrowth, 10);
192 await testResizer( W, 0, 0, 0, 0, 0);
193 await testResizer( W, 0, 0, 10, 0, -10 * ignoredGrowth);
194 await testResizer( W, 0, 10, -10, 10, 10);
195 await testResizer( W, 0, 10, 0, 10, 10 * needlessGrowth);
196 await testResizer( W, 0, 10, 10, 10, -10 * reversedGrowth);
198 // right resizer
199 await testResizer( W, H / 2, -10, -10, -10, 0);
200 await testResizer( W, H / 2, -10, 0, -10, 0);
201 await testResizer( W, H / 2, -10, 10, -10, 0);
202 await testResizer( W, H / 2, 0, -10, 0, 0);
203 await testResizer( W, H / 2, 0, 0, 0, 0);
204 await testResizer( W, H / 2, 0, 10, 0, 0);
205 await testResizer( W, H / 2, 10, -10, 10, 0);
206 await testResizer( W, H / 2, 10, 0, 10, 0);
207 await testResizer( W, H / 2, 10, 10, 10, 0);
209 // bottom right resizer
210 await testResizer( W, H, -10, -10, -10, -10);
211 await testResizer( W, H, -10, 0, -10 * ignoredGrowth, 0);
212 await testResizer( W, H, -10, 10, -10 * reversedGrowth, 10);
213 await testResizer( W, H, 0, -10, 0, -10 * ignoredGrowth);
214 await testResizer( W, H, 0, 0, 0, 0);
215 await testResizer( W, H, 0, 10, 10 * needlessGrowth, 10);
216 await testResizer( W, H, 10, -10, 10, -10 * reversedGrowth);
217 await testResizer( W, H, 10, 0, 10, 10 * needlessGrowth);
218 await testResizer( W, H, 10, 10, 10, 10);
220 // bottom resizer
221 await testResizer(W / 2, H, -10, -10, 0, -10);
222 await testResizer(W / 2, H, -10, 0, 0, 0);
223 await testResizer(W / 2, H, -10, 10, 0, 10);
224 await testResizer(W / 2, H, 0, -10, 0, -10);
225 await testResizer(W / 2, H, 0, 0, 0, 0);
226 await testResizer(W / 2, H, 0, 10, 0, 10);
227 await testResizer(W / 2, H, 10, -10, 0, -10);
228 await testResizer(W / 2, H, 10, 0, 0, 0);
229 await testResizer(W / 2, H, 10, 10, 0, 10);
231 // bottom left resizer
232 await testResizer( 0, H, -10, -10, 10, -10 * reversedGrowth);
233 await testResizer( 0, H, -10, 0, 10, 10 * needlessGrowth);
234 await testResizer( 0, H, -10, 10, 10, 10);
235 await testResizer( 0, H, 0, -10, 0, -10 * ignoredGrowth);
236 await testResizer( 0, H, 0, 0, 0, 0);
237 await testResizer( 0, H, 0, 10, 10 * needlessGrowth, 10);
238 await testResizer( 0, H, 10, -10, -10, -10);
239 await testResizer( 0, H, 10, 0, -10 * ignoredGrowth, 0);
240 await testResizer( 0, H, 10, 10, -10 * reversedGrowth, 10);
242 // left resizer
243 await testResizer( 0, H / 2, -10, -10, 10, 0);
244 await testResizer( 0, H / 2, -10, 0, 10, 0);
245 await testResizer( 0, H / 2, -10, 10, 10, 0);
246 await testResizer( 0, H / 2, 0, -10, 0, 0);
247 await testResizer( 0, H / 2, 0, 0, 0, 0);
248 await testResizer( 0, H / 2, 0, 10, 0, 0);
249 await testResizer( 0, H / 2, 10, -10, -10, 0);
250 await testResizer( 0, H / 2, 10, 0, -10, 0);
251 await testResizer( 0, H / 2, 10, 10, -10, 0);
253 // top left resizer
254 await testResizer( 0, 0, -10, -10, 10, 10);
255 await testResizer( 0, 0, -10, 0, 10, 10 * needlessGrowth);
256 await testResizer( 0, 0, -10, 10, 10, -10 * reversedGrowth);
257 await testResizer( 0, 0, 0, -10, 10 * needlessGrowth, 10);
258 await testResizer( 0, 0, 0, 0, 0, 0);
259 await testResizer( 0, 0, 0, 10, 0, -10 * ignoredGrowth);
260 await testResizer( 0, 0, 10, -10, -10 * reversedGrowth, 10);
261 await testResizer( 0, 0, 10, 0, -10 * ignoredGrowth, 0);
262 await testResizer( 0, 0, 10, 10, -10, -10);
264 /* eslint-enable no-multi-spaces */
267 const kTests = [
268 { description: "Resizers for <img>",
269 innerHTML: "<img id=\"target\" src=\"green.png\">",
270 mayPreserveRatio: true,
271 isAbsolutePosition: false,
273 { description: "Resizers for <table>",
274 innerHTML: "<table id=\"target\" border><tr><td>cell</td><td>cell</td></tr></table>",
275 mayPreserveRatio: false,
276 isAbsolutePosition: false,
278 { description: "Resizers for absolute positioned <div>",
279 innerHTML: "<div id=\"target\" style=\"position: absolute; top: 50px; left: 50px;\">positioned</div>",
280 mayPreserveRatio: false,
281 isAbsolutePosition: true,
285 // Resizers for absolute positioned element and table element are available
286 // only when enableAbsolutePositionEditing or enableInlineTableEditing is
287 // enabled for each. So, let's enable them during testing resizers for
288 // absolute positioned elements or table elements.
289 for (const kTest of kTests) {
290 document.execCommand("enableAbsolutePositionEditing", false, kTest.isAbsolutePosition);
291 await doTest(kTest.description, kTest.mayPreserveRatio, kTest.innerHTML);
293 content.innerHTML = "";
294 SimpleTest.finish();
296 </script>
297 </pre>
298 </body>
299 </html>