test_resizers_resizing_elements.html (14177B)
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; 11 } 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"; 22 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); 30 31 let outOfEditor = document.getElementById("clickaway"); 32 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); 38 39 async function waitForSelectionChange() { 40 return new Promise(resolve => { 41 document.addEventListener("selectionchange", () => { 42 resolve(); 43 }, {once: true}); 44 }); 45 } 46 47 async function doTest(aDescription, aPreserveRatio, aInnerHTML) { 48 let description = aDescription; 49 if (document.queryCommandState("enableAbsolutePositionEditing")) { 50 description += " (absolute position editor is enabled)"; 51 } 52 description += ": "; 53 content.innerHTML = aInnerHTML; 54 let target = document.getElementById("target"); 55 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. 63 */ 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(", ") + ")"); 68 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"); 75 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; 81 82 // Determine which resizer we're dealing with. 83 let basePosX = rect.width * baseX; 84 let basePosY = rect.height * baseY; 85 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; 91 } 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}`); 120 } 121 } 122 } else { 123 is(targetRanges.length, 0, 124 `getTargetRanges() of "${aEvent.type}" event for position changing of absolute position should return empty array ${aDescription}`); 125 } 126 } 127 128 content.addEventListener("beforeinput", onInput); 129 content.addEventListener("input", onInput); 130 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"}); 137 138 inputEventExpected = false; 139 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"}); 143 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; 149 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"); 157 158 content.removeEventListener("beforeinput", onInput); 159 content.removeEventListener("input", onInput); 160 } 161 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; 173 174 /* eslint-disable no-multi-spaces */ 175 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); 186 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); 197 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); 208 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); 219 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); 230 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); 241 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); 252 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); 263 264 /* eslint-enable no-multi-spaces */ 265 } 266 267 const kTests = [ 268 { description: "Resizers for <img>", 269 innerHTML: "<img id=\"target\" src=\"green.png\">", 270 mayPreserveRatio: true, 271 isAbsolutePosition: false, 272 }, 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, 277 }, 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, 282 }, 283 ]; 284 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); 292 } 293 content.innerHTML = ""; 294 SimpleTest.finish(); 295 }); 296 </script> 297 </pre> 298 </body> 299 </html>