test_mouse_enterleave_iframe.html (11408B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <title>Test mouseenter and mouseleave for iframe.</title> 4 <script src="/tests/SimpleTest/SimpleTest.js"></script> 5 <script src="/tests/SimpleTest/EventUtils.js"></script> 6 <script src="/tests/SimpleTest/paint_listener.js"></script> 7 <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> 8 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> 9 <style> 10 #start { 11 width: 300px; 12 height: 30px; 13 } 14 15 #target, #target2 { 16 width: 150px; 17 height: 150px; 18 background-color: #fcc; 19 display: inline-block; 20 } 21 22 #frame, #frame2 { 23 height: 100%; 24 width: 100%; 25 } 26 27 #reflow, #div { 28 width: 300px; 29 height: 10px; 30 background-color: lightgreen; 31 } 32 </style> 33 <div id="start">Start from here!!</div> 34 <div id="div"></div> 35 <div id="target"> 36 <iframe id="frame" frameborder="0" scrolling="no"></iframe> 37 </div> 38 <div id="target2"> 39 <iframe id="frame2" frameborder="0" scrolling="no"></iframe> 40 </div> 41 <div id="reflow"></div> 42 <script> 43 44 function reflow() { 45 let div = document.getElementById("reflow"); 46 div.style.display = "none"; 47 div.getBoundingClientRect(); 48 div.style.display = "block"; 49 div.getBoundingClientRect(); 50 } 51 52 function waitForMessage(aRemoteTarget, aEventType, aTargetName, aLastExpectedElement) { 53 return new Promise(function (aResolve, aReject) { 54 const data = `waiting for "${aEventType}" on <${aTargetName}> in <iframe id="${aRemoteTarget.id}">`; 55 let expectedMessageReceived = false; 56 window.addEventListener("message", function listener(aEvent) { 57 if (aEvent.source != aRemoteTarget.contentWindow) { 58 return; 59 } 60 61 if (aEvent.data.eventType == "reflowed") { 62 if (expectedMessageReceived) { 63 window.removeEventListener("message", listener); 64 aResolve(); 65 ok(true, `Message listener ${data} is correctly removed`); 66 } 67 return; 68 } 69 70 if (aEvent.data.eventType !== aEventType) { 71 window.removeEventListener("message", listener); 72 is( 73 aEvent.data.eventType, 74 aEventType, 75 `receive unexpected message ${JSON.stringify(aEvent.data)} at ${data}` 76 ); 77 aReject(new Error(`receive unexpected message ${JSON.stringify(aEvent.data)} at ${data}`)); 78 return; 79 } 80 81 if (aEvent.data.targetName !== aTargetName) { 82 return; 83 } 84 85 if (expectedMessageReceived) { 86 window.removeEventListener("message", listener); 87 ok(false, `receive redundant message at ${data}`); 88 aReject(new Error(`receive redundant message at ${data}`)); 89 return; 90 } 91 92 expectedMessageReceived = true; 93 ok(true, `receive message at ${data}`); 94 if (aLastExpectedElement) { 95 // Trigger a reflow which will generate synthesized mouse move event. 96 aRemoteTarget.contentWindow.postMessage("reflow", "*"); 97 } 98 }); 99 }); 100 } 101 102 /** 103 * Wait for "mouseenter" events in a child document. 104 * 105 * @param aRemoteTarget An <iframe> element which has the child document. 106 * @param aTargetNames An array of `mouseenter` targets which you want to 107 * listen to. The order should be ancestor to descendant. 108 */ 109 function waitForMouseEnterMessages(aRemoteTarget, aTargetNames) { 110 let promises = []; 111 let targetName; 112 while ((targetName = aTargetNames.shift())) { 113 promises.push( 114 waitForMessage(aRemoteTarget, "mouseenter", targetName, !aTargetNames.length) 115 ); 116 } 117 return Promise.all(promises); 118 } 119 120 /** 121 * Wait for "mouseleave" events in a child document. 122 * 123 * @param aRemoteTarget An <iframe> element which has the child document. 124 * @param aTargetNames An array of `mouseleave` targets which you want to 125 * listen to. The order should be ancestor to descendant. 126 */ 127 function waitForMouseLeaveMessages(aRemoteTarget, aTargetNames) { 128 let promises = []; 129 let targetName; 130 while ((targetName = aTargetNames.pop())) { 131 promises.push( 132 waitForMessage(aRemoteTarget, "mouseleave", targetName, !aTargetNames.length) 133 ); 134 } 135 return Promise.all(promises); 136 } 137 138 function waitForLeaveEvent(aTarget) { 139 return new Promise(function(aResolve) { 140 aTarget.addEventListener("mouseleave", function(aEvent) { 141 ok(true, `receive ${aEvent.type}`); 142 aResolve(); 143 }, { once: true }); 144 }); 145 } 146 147 function waitForEnterLeaveEvents(aEnterTarget, aLeaveTarget) { 148 let expectedEvents = [{target: aEnterTarget, eventName: "mouseenter"}]; 149 if (aLeaveTarget) { 150 expectedEvents.push({target: aLeaveTarget, eventName: "mouseleave"}) 151 } 152 153 return new Promise(function(aResolve, aReject) { 154 function cleanup() { 155 aEnterTarget.removeEventListener("mouseenter", listener); 156 aEnterTarget.removeEventListener("mouseleave", unexpectedEvent); 157 if (aLeaveTarget) { 158 aLeaveTarget.removeEventListener("mouseenter", unexpectedEvent); 159 aLeaveTarget.removeEventListener("mouseleave", listener); 160 } 161 } 162 163 function unexpectedEvent(aEvent) { 164 cleanup(); 165 ok(false, `receive unexpected ${aEvent.type}`); 166 aReject(new Error(`receive unexpected ${aEvent.type}`)); 167 } 168 169 async function listener(aEvent) { 170 if (expectedEvents.length <= 0) { 171 unexpectedEvent(aEvent); 172 return; 173 } 174 175 let expectedEvent = expectedEvents.pop(); 176 if (expectedEvent.target == aEvent.target && 177 expectedEvent.eventName == aEvent.type) { 178 ok(true, `receive ${aEvent.type}`); 179 } else { 180 unexpectedEvent(aEvent); 181 return; 182 } 183 184 if (!expectedEvents.length) { 185 // Trigger a reflow which will generate synthesized mouse move event. 186 reflow(); 187 // Now wait a bit to see if there is any unexpected event fired. 188 setTimeout(function() { 189 cleanup(); 190 aResolve(); 191 }, 0); 192 } 193 } 194 195 aEnterTarget.addEventListener("mouseenter", listener); 196 aEnterTarget.addEventListener("mouseleave", unexpectedEvent); 197 if (aLeaveTarget) { 198 aLeaveTarget.addEventListener("mouseenter", unexpectedEvent); 199 aLeaveTarget.addEventListener("mouseleave", listener); 200 } 201 }); 202 } 203 204 function moveMouseToInitialPosition() { 205 info("Mouse moves to initial position"); 206 return promiseNativeMouseEvent({ 207 type: "mousemove", 208 target: document.getElementById("start"), 209 atCenter: true, 210 }); 211 } 212 213 add_setup(async function() { 214 // Wait for focus before starting tests. 215 await SimpleTest.promiseFocus(); 216 217 // Wait for apz getting stable. 218 await waitUntilApzStable(); 219 220 // Move mouse to initial position. 221 await moveMouseToInitialPosition(); 222 223 // After initializing the mouse cursor position, we should load <iframe>s. 224 // This avoids the case that the cursor is over one of them. 225 info("Load child documents into the iframes"); 226 let promiseLoadingIFrames = []; 227 for (const iframe of document.querySelectorAll("iframe")) { 228 promiseLoadingIFrames.push( 229 new Promise(resolve => { iframe.addEventListener("load", resolve, {once: true}); }) 230 ); 231 iframe.src = "http://example.com/tests/dom/events/test/file_mouse_enterleave.html"; 232 } 233 await Promise.all(promiseLoadingIFrames); 234 }); 235 236 add_task(async function testMouseEnterLeave() { 237 let div = document.getElementById("div"); 238 let target = document.getElementById("target"); 239 let iframe = document.getElementById("frame"); 240 241 info("Mouse moves to the div above iframe"); 242 let promise = waitForEnterLeaveEvents(div); 243 synthesizeNativeMouseEvent({ 244 type: "mousemove", 245 target: div, 246 atCenter: true, 247 }); 248 await promise; 249 250 info("Mouse moves into iframe"); 251 promise = Promise.all([waitForEnterLeaveEvents(target, div), 252 waitForMouseEnterMessages(iframe, ["html", "div"])]); 253 synthesizeNativeMouseEvent({ 254 type: "mousemove", 255 target, 256 atCenter: true, 257 }); 258 await promise; 259 260 info("Mouse moves out from iframe to the div above iframe"); 261 promise = Promise.all([waitForEnterLeaveEvents(div, target), 262 waitForMouseLeaveMessages(iframe, ["html", "div"])]); 263 synthesizeNativeMouseEvent({ 264 type: "mousemove", 265 target: div, 266 atCenter: true, 267 }); 268 await promise; 269 270 // Move mouse back to initial position. This is to prevent unexpected 271 // mouseleave event in initial steps for test-verify which runs same test 272 // multiple times. 273 await moveMouseToInitialPosition(); 274 }); 275 276 add_task(async function testMouseEnterLeaveBetweenIframe() { 277 let target = document.getElementById("target"); 278 let iframe = document.getElementById("frame"); 279 280 info("Mouse moves into the first iframe"); 281 let promise = Promise.all([waitForEnterLeaveEvents(target), 282 waitForMouseEnterMessages(iframe, ["html", "div"])]); 283 synthesizeNativeMouseEvent({ 284 type: "mousemove", 285 target, 286 atCenter: true, 287 }); 288 await promise; 289 290 let target2 = document.getElementById("target2"); 291 let iframe2 = document.getElementById("frame2"); 292 293 info("Mouse moves out from the first iframe to the second iframe"); 294 promise = Promise.all([waitForEnterLeaveEvents(target2, target), 295 waitForMouseLeaveMessages(iframe, ["html", "div"]), 296 waitForMouseEnterMessages(iframe2, ["html", "div"])]); 297 synthesizeNativeMouseEvent({ 298 type: "mousemove", 299 target: target2, 300 atCenter: true, 301 }) 302 await promise; 303 304 info("Mouse moves out from the second iframe to the first iframe"); 305 promise = Promise.all([waitForEnterLeaveEvents(target, target2), 306 waitForMouseLeaveMessages(iframe2, ["html", "div"]), 307 waitForMouseEnterMessages(iframe, ["html", "div"])]); 308 synthesizeNativeMouseEvent({ 309 type: "mousemove", 310 target, 311 atCenter: true, 312 }); 313 await promise; 314 315 // Move mouse back to initial position. 316 await Promise.all([waitForLeaveEvent(target), 317 waitForMouseLeaveMessages(iframe, ["html", "div"]), 318 moveMouseToInitialPosition()]); 319 }); 320 321 add_task(async function testMouseEnterLeaveSwitchWindow() { 322 let target = document.getElementById("target"); 323 let iframe = document.getElementById("frame"); 324 325 info("Mouse moves into iframe"); 326 let promise = Promise.all([waitForEnterLeaveEvents(target), 327 waitForMouseEnterMessages(iframe, ["html", "div"])]); 328 synthesizeNativeMouseEvent({ 329 type: "mousemove", 330 target, 331 atCenter: true, 332 }); 333 await promise; 334 335 info("Open and switch to new window"); 336 promise = Promise.all([waitForLeaveEvent(target), 337 waitForMouseLeaveMessages(iframe, ["html", "div"])]); 338 let win = window.open("http://example.com/tests/dom/events/test/file_mouse_enterleave.html"); 339 // Trigger a reflow which will generate synthesized mouse move event. 340 win.postMessage("reflow", "*"); 341 await promise; 342 343 info("Switch back to test window"); 344 promise = Promise.all([waitForEnterLeaveEvents(target), 345 waitForMouseEnterMessages(iframe, ["html", "div"])]); 346 win.close(); 347 // Trigger a reflow which will generate synthesized mouse move event. 348 reflow(); 349 // Wait for apz getting stable. 350 await waitUntilApzStable(); 351 synthesizeNativeMouseEvent({ 352 type: "mousemove", 353 target, 354 atCenter: true, 355 }); 356 await promise; 357 358 // Move mouse back to initial position. 359 await Promise.all([waitForLeaveEvent(target), 360 moveMouseToInitialPosition()]); 361 }); 362 </script>