<!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1034110 --> <head> <title>Test for Bug 1034110</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1034110">Mozilla Bug 1034110</a> <style type="text/css"> #pseudo.before::before { content: "before"; } #pseudo.after::after { content: "after"; } </style> <div id="pseudo"></div> <video id="video"></video> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script type="application/javascript;version=1.7"> /** Test for Bug 1034110 **/ SimpleTest.waitForExplicitFinish(); function getWalker(node) { return SpecialPowers.createDOMWalker(node, true); } function getFirstChild(parent) { return SpecialPowers.unwrap(getWalker(parent).firstChild); } function getLastChild(parent) { return SpecialPowers.unwrap(getWalker(parent).lastChild); } function assertSamePseudoElement(which, node1, node2) { is(SpecialPowers.wrap(node1).nodeName, "_moz_generated_content_" + which, "Correct pseudo element type"); is(node1, node2, "Referencing the same ::after element"); } window.onload = function () { testOneAdded(); }; function testOneAdded() { let parent = document.getElementById("pseudo"); var m = new MutationObserver(function(records, observer) { is(records.length, 1, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type"); is(records[0].target, parent, "Correct target"); is(records[0].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent)); is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes"); observer.disconnect(); testAddedAndRemoved(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.className = "before"; } function testAddedAndRemoved() { let parent = document.getElementById("pseudo"); let originalBeforeElement = getFirstChild(parent); var m = new MutationObserver(function(records, observer) { is(records.length, 2, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)"); is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)"); is(records[0].target, parent, "Correct target (1)"); is(records[1].target, parent, "Correct target (2)"); // Two records are sent - one for removed and one for added. is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes"); is(records[0].removedNodes.length, 1, "Should have got removedNodes"); assertSamePseudoElement("before", records[0].removedNodes[0], originalBeforeElement); is(records[1].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent)); is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes"); observer.disconnect(); testRemoved(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.className = "after"; } function testRemoved() { let parent = document.getElementById("pseudo"); let originalAfterElement = getLastChild(parent); var m = new MutationObserver(function(records, observer) { is(records.length, 1, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type"); is(records[0].target, parent, "Correct target"); is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes"); is(records[0].removedNodes.length, 1, "Should have got removedNodes"); assertSamePseudoElement("after", records[0].removedNodes[0], originalAfterElement); observer.disconnect(); testMultipleAdded(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.className = ""; } function testMultipleAdded() { let parent = document.getElementById("pseudo"); var m = new MutationObserver(function(records, observer) { is(records.length, 2, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)"); is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)"); is(records[0].target, parent, "Correct target (1)"); is(records[1].target, parent, "Correct target (2)"); is(records[0].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent)); is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes"); is(records[1].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent)); is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes"); observer.disconnect(); testRemovedDueToDisplay(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.className = "before after"; } function testRemovedDueToDisplay() { let parent = document.getElementById("pseudo"); let originalBeforeElement = getFirstChild(parent); let originalAfterElement = getLastChild(parent); var m = new MutationObserver(function(records, observer) { is(records.length, 2, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)"); is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)"); is(records[0].target, parent, "Correct target (1)"); is(records[1].target, parent, "Correct target (2)"); is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes"); is(records[0].removedNodes.length, 1, "Should have got removedNodes"); assertSamePseudoElement("before", records[0].removedNodes[0], originalBeforeElement); is(records[1].addedNodes.length, 0, "Shouldn't have got addedNodes"); is(records[1].removedNodes.length, 1, "Should have got removedNodes"); assertSamePseudoElement("after", records[1].removedNodes[0], originalAfterElement); observer.disconnect(); testAddedDueToDisplay(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.style.display = "none"; } function testAddedDueToDisplay() { let parent = document.getElementById("pseudo"); var m = new MutationObserver(function(records, observer) { is(records.length, 2, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)"); is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)"); is(records[0].target, parent, "Correct target (1)"); is(records[1].target, parent, "Correct target (2)"); is(records[0].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent)); is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes"); is(records[1].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent)); is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes"); observer.disconnect(); testDifferentTargetNoSubtree(); }); SpecialPowers.observeMutationEvents(m, parent, true); parent.style.display = "block"; } function testDifferentTargetNoSubtree() { let parent = document.getElementById("pseudo"); var m = new MutationObserver(function(records, observer) { ok(false, "No mutation should fire when observing on a parent without subtree option."); }); SpecialPowers.observeMutationEvents(m, document, true); parent.style.display = "none"; // Wait for the actual mutation to come through, making sure that // the original observer never fires. var m2 = new MutationObserver(function(records, observer) { ok(!getFirstChild(parent), "Pseudo element has been removed, but no mutation"); ok(!getLastChild(parent), "Pseudo element has been removed, but no mutation"); observer.disconnect(); testSubtree(); }); SpecialPowers.observeMutationEvents(m2, parent, true); } function testSubtree() { let parent = document.getElementById("pseudo"); var m = new MutationObserver(function(records, observer) { is(records.length, 2, "Correct number of records"); is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)"); is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)"); is(records[0].target, parent, "Correct target (1)"); is(records[1].target, parent, "Correct target (2)"); is(records[0].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent)); is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes"); is(records[1].addedNodes.length, 1, "Should have got addedNodes"); assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent)); is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes"); observer.disconnect(); testDictionaryWithoutChromePriv(); }); SpecialPowers.observeMutationEvents(m, document, true, true); parent.style.display = "block"; } function testDictionaryWithoutChromePriv() { var m = new MutationObserver(function() {}); try { m.observe(document, { childList: true, get nativeAnonymousChildList() { throw "Foo1"; } } ); ok(true, "Shouldn't throw!"); } catch(ex) { ok(false, "Did throw " + ex); } try { m.observe(document, { childList: true, get animations() { throw "Foo2"; } } ); ok(true, "Shouldn't throw!"); } catch(ex) { ok(false, "Did throw " + ex); } SimpleTest.finish(); } </script> </pre> </body> </html>