<!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=542058 --> <head> <title>Test for MediaQueryList (Bug 542058)</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body onload="run()"> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=542058">Mozilla Bug 542058</a> <iframe id="subdoc" src="about:blank"></iframe> <div id="content" style="display:none"></div> <pre id="test"> <script type="application/javascript"> /** Test for MediaQueryList (Bug 542058) **/ SimpleTest.waitForExplicitFinish(); function run() { var iframe = document.getElementById("subdoc"); var subdoc = iframe.contentDocument; var subwin = iframe.contentWindow; var subroot = subdoc.documentElement; var content_div = document.getElementById("content"); content_div.style.font = "initial"; var em_size = getComputedStyle(content_div, "").fontSize.match(/^(\d+)px$/)[1]; var w = Math.floor(em_size * 9.3); var h = Math.floor(em_size * 4.2); iframe.style.width = w + "px"; iframe.style.height = h + "px"; subroot.offsetWidth; // flush layout function setup_mql(str) { var obj = { str: str, mql: subwin.matchMedia(str), notifyCount: 0, listener: function(mql) { is(mql, obj.mql, "correct argument to listener: " + obj.str); ++obj.notifyCount; // Test the last match result only on odd // notifications. if (obj.notifyCount & 1) { obj.lastOddMatchResult = mql.matches; } } } obj.mql.addListener(obj.listener); return obj; } function finish_mql(obj) { obj.mql.removeListener(obj.listener); } var w_exact_w = setup_mql("(width: " + w + "px)"); var w_min_9em = setup_mql("(min-width : 9em)"); var w_min_10em = setup_mql("( min-width: 10em ) "); var w_max_9em = setup_mql("(max-width: 9em)"); var w_max_10em = setup_mql("(max-width: 10em)"); is(w_exact_w.mql.media, "(width: " + w + "px)", "serialization"); is(w_min_9em.mql.media, "(min-width: 9em)", "serialization"); is(w_min_10em.mql.media, "(min-width: 10em)", "serialization"); is(w_max_9em.mql.media, "(max-width: 9em)", "serialization"); is(w_max_10em.mql.media, "(max-width: 10em)", "serialization"); function check_match(obj, expected, desc) { is(obj.mql.matches, expected, obj.str + " media query list .matches " + desc); if (obj.notifyCount & 1) { // odd notifications only is(obj.lastOddMatchResult, expected, obj.str + " media query list last notify result " + desc); } } function check_notify(obj, expected, desc) { is(obj.notifyCount, expected, obj.str + " media query list .notify count " + desc); } check_match(w_exact_w, true, "initially"); check_notify(w_exact_w, 0, "initially"); check_match(w_min_9em, true, "initially"); check_notify(w_min_9em, 0, "initially"); check_match(w_min_10em, false, "initially"); check_notify(w_min_10em, 0, "initially"); check_match(w_max_9em, false, "initially"); check_notify(w_max_9em, 0, "initially"); check_match(w_max_10em, true, "initially"); check_notify(w_max_10em, 0, "initially"); var w2 = Math.floor(em_size * 10.3); iframe.style.width = w2 + "px"; subroot.offsetWidth; // flush layout check_match(w_exact_w, false, "after width increase to around 10.3em"); check_notify(w_exact_w, 1, "after width increase to around 10.3em"); check_match(w_min_9em, true, "after width increase to around 10.3em"); check_notify(w_min_9em, 0, "after width increase to around 10.3em"); check_match(w_min_10em, true, "after width increase to around 10.3em"); check_notify(w_min_10em, 1, "after width increase to around 10.3em"); check_match(w_max_9em, false, "after width increase to around 10.3em"); check_notify(w_max_9em, 0, "after width increase to around 10.3em"); check_match(w_max_10em, false, "after width increase to around 10.3em"); check_notify(w_max_10em, 1, "after width increase to around 10.3em"); var w3 = w * 2; iframe.style.width = w3 + "px"; subroot.offsetWidth; // flush layout check_match(w_exact_w, false, "after width double from original"); check_notify(w_exact_w, 1, "after width double from original"); check_match(w_min_9em, true, "after width double from original"); check_notify(w_min_9em, 0, "after width double from original"); check_match(w_min_10em, true, "after width double from original"); check_notify(w_min_10em, 1, "after width double from original"); check_match(w_max_9em, false, "after width double from original"); check_notify(w_max_9em, 0, "after width double from original"); check_match(w_max_10em, false, "after width double from original"); check_notify(w_max_10em, 1, "after width double from original"); SpecialPowers.setFullZoom(subwin, 2.0); subroot.offsetWidth; // flush layout check_match(w_exact_w, true, "after zoom"); check_notify(w_exact_w, 2, "after zoom"); check_match(w_min_9em, true, "after zoom"); check_notify(w_min_9em, 0, "after zoom"); check_match(w_min_10em, false, "after zoom"); check_notify(w_min_10em, 2, "after zoom"); check_match(w_max_9em, false, "after zoom"); check_notify(w_max_9em, 0, "after zoom"); check_match(w_max_10em, true, "after zoom"); check_notify(w_max_10em, 2, "after zoom"); SpecialPowers.setFullZoom(subwin, 1.0); finish_mql(w_exact_w); finish_mql(w_min_9em); finish_mql(w_min_10em); finish_mql(w_max_9em); finish_mql(w_max_10em); // Additional tests of listener mutation. (function() { var received = []; var received_mql = []; function listener1(mql) { received.push(1); received_mql.push(mql); } function listener2(mql) { received.push(2); received_mql.push(mql); } iframe.style.width = "200px"; subroot.offsetWidth; // flush layout var mql = subwin.matchMedia("(min-width: 150px)"); mql.addListener(listener1); mql.addListener(listener1); mql.addListener(listener2); is(JSON.stringify(received), "[]", "listeners before notification"); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout is(JSON.stringify(received), "[1,2]", "duplicate listeners removed"); received = []; mql.removeListener(listener1); iframe.style.width = "200px"; subroot.offsetWidth; // flush layout is(JSON.stringify(received), "[2]", "listener removal"); received = []; mql.addListener(listener1); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout is(JSON.stringify(received), "[2,1]", "listeners notified in order"); received = []; mql.addListener(listener2); iframe.style.width = "200px"; subroot.offsetWidth; // flush layout is(JSON.stringify(received), "[2,1]", "add of existing listener is no-op"); received = []; mql.addListener(listener1); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout is(JSON.stringify(received), "[2,1]", "add of existing listener is no-op"); mql.removeListener(listener2); received = []; received_mql = []; var mql2 = subwin.matchMedia("(min-width: 160px)"); mql2.addListener(listener1); mql.addListener(listener2); iframe.style.width = "200px"; subroot.offsetWidth; // flush layout // mql (1, 2), mql2 (1) is(JSON.stringify(received), "[1,2,1]", "notification of lists in order created"); is(received_mql[0], mql, "notification of lists in order created"); is(received_mql[1], mql, "notification of lists in order created"); is(received_mql[2], mql2, "notification of lists in order created"); received = []; received_mql = []; function removing_listener(mql) { received.push(3); received_mql.push(mql); mql.removeListener(listener2); mql2.removeListener(listener1); } mql.addListener(removing_listener); mql.removeListener(listener2); mql.addListener(listener2); // after removing_listener (3) iframe.style.width = "100px"; subroot.offsetWidth; // flush layout // mql(1, 3, 2) mql2(1) is(JSON.stringify(received), "[1,3,2,1]", "listeners still notified after removed if change was before"); is(received_mql[0], mql, "notification order (removal tests)"); is(received_mql[1], mql, "notification order (removal tests)"); is(received_mql[2], mql, "notification order (removal tests)"); is(received_mql[3], mql2, "notification order (removal tests)"); received = []; received_mql = []; iframe.style.width = "200px"; subroot.offsetWidth; // flush layout // mql(1, 3) is(JSON.stringify(received), "[1,3]", "listeners not notified for changes after their removal"); is(received_mql[0], mql, "notification order (removal tests)"); is(received_mql[1], mql, "notification order (removal tests)"); })(); /* Bug 716751: null-dereference crash */ (function() { iframe.style.width = "200px"; subroot.offsetWidth; // flush layout var mql = subwin.matchMedia("(min-width: 150px)"); SimpleTest.doesThrow(function() { mql.addListener(null); }, "expected an exception"); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout // With the bug, we crash here. No need for test assertions. SimpleTest.doesThrow(function() { mql.removeListener(null); }, "expected an exception"); SimpleTest.doesThrow(function() { mql.removeListener(null); }, "expected an exception"); })(); /* Bug 753777: test that things work in a freshly-created iframe */ (function() { var iframe = document.createElement("iframe"); document.body.appendChild(iframe); is(iframe.contentWindow.matchMedia("(min-width: 1px)").matches, true, "(min-width: 1px) should match in newly-created iframe"); is(iframe.contentWindow.matchMedia("(max-width: 1px)").matches, false, "(max-width: 1px) should not match in newly-created iframe"); document.body.removeChild(iframe); })(); /* Bug 716751: listeners lost due to GC */ var gc_received = []; (function() { var received = []; var listener1 = function(mql) { gc_received.push(1); } iframe.style.width = "200px"; subroot.offsetWidth; // flush layout var mql = subwin.matchMedia("(min-width: 150px)"); mql.addListener(listener1); is(JSON.stringify(gc_received), "[]", "GC test: before notification"); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout is(JSON.stringify(gc_received), "[1]", "GC test: after notification 1"); // Because of conservative GC, we need to go back to the event loop // to GC properly. setTimeout(step2, 0); })(); function step2() { SpecialPowers.DOMWindowUtils.garbageCollect(); iframe.style.width = "200px"; subroot.offsetWidth; // flush layout is(JSON.stringify(gc_received), "[1,1]", "GC test: after notification 2"); bug1270626(); } /* Bug 1270626: listeners that throw exceptions */ function bug1270626() { var throwingListener = function(mql) { throw "error"; } iframe.style.width = "200px"; subroot.offsetWidth; // flush layout var mql = subwin.matchMedia("(min-width: 150px)"); mql.addListener(throwingListener); SimpleTest.expectUncaughtException(true); is(SimpleTest.isExpectingUncaughtException(), true, "should be waiting for an uncaught exception"); iframe.style.width = "100px"; subroot.offsetWidth; // flush layout is(SimpleTest.isExpectingUncaughtException(), false, "should have gotten an uncaught exception"); SimpleTest.finish(); } } </script> </pre> </body> </html>