test_media_query_list.html (12230B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=542058 5 --> 6 <head> 7 <title>Test for MediaQueryList (Bug 542058)</title> 8 <script src="/tests/SimpleTest/SimpleTest.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body onload="run()"> 12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=542058">Mozilla Bug 542058</a> 13 <iframe id="subdoc" src="about:blank"></iframe> 14 <div id="content" style="display:none"></div> 15 <pre id="test"> 16 <script type="application/javascript"> 17 18 /** Test for MediaQueryList (Bug 542058) */ 19 20 SimpleTest.waitForExplicitFinish(); 21 22 function tick() { 23 // MediaQueryList events are guaranteed to run before requestAnimationFrame 24 // per spec. 25 return new Promise(r => requestAnimationFrame(r)); 26 } 27 28 async function run() { 29 var iframe = document.getElementById("subdoc"); 30 var subdoc = iframe.contentDocument; 31 var subwin = iframe.contentWindow; 32 var subroot = subdoc.documentElement; 33 34 var content_div = document.getElementById("content"); 35 content_div.style.font = "initial"; 36 var em_size = 37 getComputedStyle(content_div).fontSize.match(/^(\d+)px$/)[1]; 38 39 var w = Math.floor(em_size * 9.3); 40 var h = Math.floor(em_size * 4.2); 41 iframe.style.width = w + "px"; 42 iframe.style.height = h + "px"; 43 subroot.offsetWidth; // flush layout 44 await tick(); 45 46 function setup_mql(str) { 47 var obj = { 48 str: str, 49 mql: subwin.matchMedia(str), 50 notifyCount: 0, 51 listener: function(event) { 52 ok(event instanceof subwin.MediaQueryListEvent, 53 "correct argument to listener: " + obj.str); 54 is(event.media, obj.mql.media, 55 "correct media in the event: " + obj.str); 56 is(event.target, obj.mql, 57 "correct target in the event: " + obj.str); 58 ++obj.notifyCount; 59 // Test the last match result only on odd 60 // notifications. 61 if (obj.notifyCount & 1) { 62 obj.lastOddMatchResult = event.target.matches; 63 } 64 } 65 } 66 obj.mql.addListener(obj.listener); 67 return obj; 68 } 69 70 function finish_mql(obj) { 71 obj.mql.removeListener(obj.listener); 72 } 73 74 var w_exact_w = setup_mql("(width: " + w + "px)"); 75 var w_min_9em = setup_mql("(min-width : 9em)"); 76 var w_min_10em = setup_mql("( min-width: 10em ) "); 77 var w_max_9em = setup_mql("(max-width: 9em)"); 78 var w_max_10em = setup_mql("(max-width: 10em)"); 79 80 is(w_exact_w.mql.media, "(width: " + w + "px)", "serialization"); 81 is(w_min_9em.mql.media, "(min-width: 9em)", "serialization"); 82 is(w_min_10em.mql.media, "(min-width: 10em)", "serialization"); 83 is(w_max_9em.mql.media, "(max-width: 9em)", "serialization"); 84 is(w_max_10em.mql.media, "(max-width: 10em)", "serialization"); 85 86 function check_match(obj, expected, desc) { 87 is(obj.mql.matches, expected, 88 obj.str + " media query list .matches " + desc); 89 if (obj.notifyCount & 1) { // odd notifications only 90 is(obj.lastOddMatchResult, expected, 91 obj.str + " media query list last notify result " + desc); 92 } 93 } 94 function check_notify(obj, expected, desc) { 95 is(obj.notifyCount, expected, 96 obj.str + " media query list .notify count " + desc); 97 } 98 check_match(w_exact_w, true, "initially"); 99 check_notify(w_exact_w, 0, "initially"); 100 check_match(w_min_9em, true, "initially"); 101 check_notify(w_min_9em, 0, "initially"); 102 check_match(w_min_10em, false, "initially"); 103 check_notify(w_min_10em, 0, "initially"); 104 check_match(w_max_9em, false, "initially"); 105 check_notify(w_max_9em, 0, "initially"); 106 check_match(w_max_10em, true, "initially"); 107 check_notify(w_max_10em, 0, "initially"); 108 109 var w2 = Math.floor(em_size * 10.3); 110 iframe.style.width = w2 + "px"; 111 subroot.offsetWidth; // flush layout 112 await tick(); 113 114 check_match(w_exact_w, false, "after width increase to around 10.3em"); 115 check_notify(w_exact_w, 1, "after width increase to around 10.3em"); 116 check_match(w_min_9em, true, "after width increase to around 10.3em"); 117 check_notify(w_min_9em, 0, "after width increase to around 10.3em"); 118 check_match(w_min_10em, true, "after width increase to around 10.3em"); 119 check_notify(w_min_10em, 1, "after width increase to around 10.3em"); 120 check_match(w_max_9em, false, "after width increase to around 10.3em"); 121 check_notify(w_max_9em, 0, "after width increase to around 10.3em"); 122 check_match(w_max_10em, false, "after width increase to around 10.3em"); 123 check_notify(w_max_10em, 1, "after width increase to around 10.3em"); 124 125 var w3 = w * 2; 126 iframe.style.width = w3 + "px"; 127 subroot.offsetWidth; // flush layout 128 await tick(); 129 130 check_match(w_exact_w, false, "after width double from original"); 131 check_notify(w_exact_w, 1, "after width double from original"); 132 check_match(w_min_9em, true, "after width double from original"); 133 check_notify(w_min_9em, 0, "after width double from original"); 134 check_match(w_min_10em, true, "after width double from original"); 135 check_notify(w_min_10em, 1, "after width double from original"); 136 check_match(w_max_9em, false, "after width double from original"); 137 check_notify(w_max_9em, 0, "after width double from original"); 138 check_match(w_max_10em, false, "after width double from original"); 139 check_notify(w_max_10em, 1, "after width double from original"); 140 141 SpecialPowers.setFullZoom(subwin, 2.0); 142 subroot.offsetWidth; // flush layout 143 await tick(); 144 145 check_match(w_exact_w, true, "after zoom"); 146 check_notify(w_exact_w, 2, "after zoom"); 147 check_match(w_min_9em, true, "after zoom"); 148 check_notify(w_min_9em, 0, "after zoom"); 149 check_match(w_min_10em, false, "after zoom"); 150 check_notify(w_min_10em, 2, "after zoom"); 151 check_match(w_max_9em, false, "after zoom"); 152 check_notify(w_max_9em, 0, "after zoom"); 153 check_match(w_max_10em, true, "after zoom"); 154 check_notify(w_max_10em, 2, "after zoom"); 155 156 SpecialPowers.setFullZoom(subwin, 1.0); 157 158 await tick(); 159 160 finish_mql(w_exact_w); 161 finish_mql(w_min_9em); 162 finish_mql(w_min_10em); 163 finish_mql(w_max_9em); 164 finish_mql(w_max_10em); 165 166 // Additional tests of listener mutation. 167 { 168 let received = []; 169 let received_mql = []; 170 function listener1(event) { 171 received.push(1); 172 received_mql.push(event.target); 173 } 174 function listener2(event) { 175 received.push(2); 176 received_mql.push(event.target); 177 } 178 179 iframe.style.width = "200px"; 180 subroot.offsetWidth; // flush layout 181 await tick(); 182 183 let mql = subwin.matchMedia("(min-width: 150px)"); 184 mql.addListener(listener1); 185 mql.addListener(listener1); 186 mql.addListener(listener2); 187 is(JSON.stringify(received), "[]", "listeners before notification"); 188 189 iframe.style.width = "100px"; 190 subroot.offsetWidth; // flush layout 191 await tick(); 192 193 is(JSON.stringify(received), "[1,2]", "duplicate listeners removed"); 194 received = []; 195 mql.removeListener(listener1); 196 197 iframe.style.width = "200px"; 198 subroot.offsetWidth; // flush layout 199 await tick(); 200 201 is(JSON.stringify(received), "[2]", "listener removal"); 202 received = []; 203 mql.addListener(listener1); 204 205 iframe.style.width = "100px"; 206 subroot.offsetWidth; // flush layout 207 await tick(); 208 209 is(JSON.stringify(received), "[2,1]", "listeners notified in order"); 210 received = []; 211 mql.addListener(listener2); 212 213 iframe.style.width = "200px"; 214 subroot.offsetWidth; // flush layout 215 await tick(); 216 217 is(JSON.stringify(received), "[2,1]", "add of existing listener is no-op"); 218 received = []; 219 mql.addListener(listener1); 220 221 iframe.style.width = "100px"; 222 subroot.offsetWidth; // flush layout 223 await tick(); 224 225 is(JSON.stringify(received), "[2,1]", "add of existing listener is no-op"); 226 mql.removeListener(listener2); 227 received = []; 228 received_mql = []; 229 230 var mql2 = subwin.matchMedia("(min-width: 160px)"); 231 mql2.addListener(listener1); 232 mql.addListener(listener2); 233 234 iframe.style.width = "200px"; 235 subroot.offsetWidth; // flush layout 236 await tick(); 237 238 // mql (1, 2), mql2 (1) 239 is(JSON.stringify(received), "[1,2,1]", 240 "notification of lists in order created"); 241 is(received_mql[0], mql, 242 "notification of lists in order created"); 243 is(received_mql[1], mql, 244 "notification of lists in order created"); 245 is(received_mql[2], mql2, 246 "notification of lists in order created"); 247 received = []; 248 received_mql = []; 249 250 function removing_listener(event) { 251 received.push(3); 252 received_mql.push(event.target); 253 event.target.removeListener(listener2); 254 mql2.removeListener(listener1); 255 } 256 257 mql.addListener(removing_listener); 258 mql.removeListener(listener2); 259 mql.addListener(listener2); // after removing_listener (3) 260 261 iframe.style.width = "100px"; 262 subroot.offsetWidth; // flush layout 263 await tick(); 264 265 // mql(1, 3) 266 is(JSON.stringify(received), "[1,3]", 267 "listeners still notified after removed if change was before"); 268 is(received_mql[0], mql, 269 "notification order (removal tests)"); 270 is(received_mql[1], mql, 271 "notification order (removal tests)"); 272 received = []; 273 received_mql = []; 274 275 iframe.style.width = "200px"; 276 subroot.offsetWidth; // flush layout 277 await tick(); 278 279 // mql(1, 3) 280 is(JSON.stringify(received), "[1,3]", 281 "listeners not notified for changes after their removal"); 282 is(received_mql[0], mql, 283 "notification order (removal tests)"); 284 is(received_mql[1], mql, 285 "notification order (removal tests)"); 286 } 287 288 /* Bug 753777: test that things work in a freshly-created iframe */ 289 { 290 let newIframe = document.createElement("iframe"); 291 document.body.appendChild(newIframe); 292 293 is(newIframe.contentWindow.matchMedia("all").matches, true, 294 "matchMedia should work in newly-created iframe"); 295 // Passes due to initial about:blank poking layout. 296 // See https://github.com/w3c/csswg-drafts/issues/3101, bug 1458816, 297 // and bug 1011468. 298 is(newIframe.contentWindow.matchMedia("(min-width: 1px)").matches, true, 299 "(min-width: 1px) should match in newly-created iframe"); 300 is(newIframe.contentWindow.matchMedia("(max-width: 1px)").matches, false, 301 "(max-width: 1px) should not match in newly-created iframe"); 302 303 document.body.removeChild(newIframe); 304 } 305 306 /* Bug 716751: listeners lost due to GC */ 307 var gc_received = []; 308 { 309 let received = []; 310 let listener1 = function(event) { 311 gc_received.push(1); 312 } 313 314 iframe.style.width = "200px"; 315 subroot.offsetWidth; // flush layout 316 await tick(); 317 318 let mql = subwin.matchMedia("(min-width: 150px)"); 319 mql.addListener(listener1); 320 is(JSON.stringify(gc_received), "[]", "GC test: before notification"); 321 322 iframe.style.width = "100px"; 323 subroot.offsetWidth; // flush layout 324 await tick(); 325 326 is(JSON.stringify(gc_received), "[1]", "GC test: after notification 1"); 327 328 // Because of conservative GC, we need to go back to the event loop 329 // to GC properly. 330 setTimeout(step2, 0); 331 } 332 333 async function step2() { 334 SpecialPowers.DOMWindowUtils.garbageCollect(); 335 336 iframe.style.width = "200px"; 337 subroot.offsetWidth; // flush layout 338 await tick(); 339 340 is(JSON.stringify(gc_received), "[1,1]", "GC test: after notification 2"); 341 342 bug1270626(); 343 } 344 345 /* Bug 1270626: listeners that throw exceptions */ 346 async function bug1270626() { 347 var throwingListener = function(event) { 348 throw "error"; 349 } 350 351 iframe.style.width = "200px"; 352 subroot.offsetWidth; // flush layout 353 await tick(); 354 355 var mql = subwin.matchMedia("(min-width: 150px)"); 356 mql.addListener(throwingListener); 357 358 SimpleTest.expectUncaughtException(true); 359 is(SimpleTest.isExpectingUncaughtException(), true, 360 "should be waiting for an uncaught exception"); 361 362 iframe.style.width = "100px"; 363 subroot.offsetWidth; // flush layout 364 await tick(); 365 366 is(SimpleTest.isExpectingUncaughtException(), false, 367 "should have gotten an uncaught exception"); 368 369 SimpleTest.finish(); 370 } 371 } 372 373 </script> 374 </pre> 375 </body> 376 </html>