Bug 1914513 - Add a pref to disable mutation events, r=masayuki

Differential Revision: https://phabricator.services.mozilla.com/D219934
This commit is contained in:
Olli Pettay 2024-09-02 13:29:57 +00:00
parent b4ad683950
commit e1fad67026
49 changed files with 997 additions and 876 deletions

View File

@ -2,20 +2,24 @@
<head>
<title>testcase2 Bug 432114 <20> Crash [@ PL_DHashTableOperate] with DOMNodeInserted event listener removing window and frameset contenteditable</title>
</head>
<body>
<body onload="SpecialPowers.pushPrefEnv({'set': [['dom.mutation_events.enabled', true]]}, run);">
<script>
window.addEventListener("DOMNodeRemoved", function() {
setTimeout(function() {
document.documentElement.removeAttribute("class");
}, 0);
});
function run() {
var iframe = document.getElementById("content");
iframe.onload=function() {
dump("iframe onload\n");
console.log("iframe onload");
};
iframe.src = "file_432114-2.xhtml";
}
</script>
<iframe id="content" src="file_432114-2.xhtml" style="width:1000px;height: 200px;"></iframe>
<iframe id="content" style="width:1000px;height: 200px;"></iframe>
</body>
</html>

View File

@ -7908,6 +7908,14 @@ void Document::SetScopeObject(nsIGlobalObject* aGlobal) {
#ifdef DEBUG
AssertDocGroupMatchesKey();
#endif
// Update data document's mMutationEventsEnabled early on so that
// we can avoid extra IsURIInPrefList calls.
if (mMutationEventsEnabled.isNothing()) {
mMutationEventsEnabled.emplace(
window->GetExtantDoc()->MutationEventsEnabled());
}
return;
}
@ -19595,4 +19603,15 @@ already_AddRefed<Document> Document::ParseHTMLUnsafe(GlobalObject& aGlobal,
return doc.forget();
}
bool Document::MutationEventsEnabled() {
if (StaticPrefs::dom_mutation_events_enabled()) {
return true;
}
if (mMutationEventsEnabled.isNothing()) {
mMutationEventsEnabled.emplace(
NodePrincipal()->IsURIInPrefList("dom.mutation_events.forceEnable"));
}
return mMutationEventsEnabled.value();
}
} // namespace mozilla::dom

View File

@ -3656,6 +3656,10 @@ class Document : public nsINode,
bool FireMutationEvents() const { return mFireMutationEvents; }
void SetFireMutationEvents(bool aFire) { mFireMutationEvents = aFire; }
// Even if mutation events are disabled by default,
// dom.mutation_events.forceEnable can be used to enable them per site.
bool MutationEventsEnabled();
// This should be called when this document receives events which are likely
// to be user interaction with the document, rather than the byproduct of
// interaction with the browser (i.e. a keypress to scroll the view port,
@ -4924,6 +4928,8 @@ class Document : public nsINode,
bool mFireMutationEvents : 1;
Maybe<bool> mMutationEventsEnabled;
// The fingerprinting protections overrides for this document. The value will
// override the default enabled fingerprinting protections for this document.
// This will only get populated if these is one that comes from the local

View File

@ -25,7 +25,7 @@ function boom()
</head>
<body onload="setTimeout(boom, 30)">
<body onload="SpecialPowers.pushPrefEnv({'set': [['dom.mutation_events.enabled', true]]}, () => setTimeout(boom, 30))">
<div id="n"></div>

View File

@ -5205,6 +5205,10 @@ bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
bool nsContentUtils::WantMutationEvents(nsINode* aNode, uint32_t aType,
nsINode* aTargetForSubtreeModified) {
Document* doc = aNode->OwnerDoc();
if (!doc->MutationEventsEnabled()) {
return false;
}
if (!doc->FireMutationEvents()) {
return false;
}
@ -11283,13 +11287,13 @@ bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
return false;
}
nsAutoCString scheme;
aURI->GetScheme(scheme);
if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
if (aList.IsEmpty()) {
return false;
}
if (aList.IsEmpty()) {
nsAutoCString scheme;
aURI->GetScheme(scheme);
if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
return false;
}

View File

@ -18,6 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1075702
<script type="application/javascript">
/** Test for Bug 1075702 **/
function test() {
// test: Element.removeAttributeNode()
var a1 = document.createAttribute("aa");
@ -71,7 +72,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1075702
}
testremoveNamedItem();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</body>
</html>

View File

@ -6,7 +6,7 @@
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script>
add_task(t => {
function test() {
let root = document.createElement("div");
root.innerHTML = "<div id='a'>text<div id='b'>text2</div></div>";
const a = root.firstChild;
@ -66,7 +66,11 @@
is(events[2].relatedNode, b);
is(events[3].target, dfChild2);
is(events[3].relatedNode, b);
});
SimpleTest.finish();
};
SimpleTest.waitForExplicitFinish();
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test));
</script>
</head>
<body>

View File

@ -18,8 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=339494
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 339494 **/
/** Test for Bug 339494 **/
function test() {
var d = document.getElementById("d");
d.setAttribute("hhh", "testvalue");
@ -51,6 +51,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=339494
isnot(s.getAttribute("ggg"), "testvalue", "Value check 4");
is(s.getAttribute("ggg"), "othervalue", "Value check 5");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</pre>

View File

@ -18,7 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=339494
<script class="testbody" type="text/javascript">
/** Test for Bug 339494 **/
function test() {
var d = document.getElementById("d");
d.setAttribute("hhh", "testvalue");
@ -50,6 +50,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=339494
isnot(s.getAttribute("ggg"), "testvalue", "Value check 4");
is(s.getAttribute("ggg"), "othervalue", "Value check 5");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</pre>

View File

@ -64,9 +64,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=362391
expected = "null";
document.getElementById("test4")
.removeAttribute("attr");
SimpleTest.finish();
}
test();
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</pre>

View File

@ -35,9 +35,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=364092
test1.addEventListener("DOMAttrModified", mutationHandler, true);
test1.removeAttributeNode(attrNode);
test1.removeEventListener("DOMAttrModified", mutationHandler, true);
SimpleTest.finish();
}
runTest();
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, runTest);
</script>
</pre>

View File

@ -37,9 +37,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=364413
test1.addEventListener("DOMAttrModified", mutationHandler, true);
test1.setAttributeNodeNS(attrNode);
test1.removeEventListener("DOMAttrModified", mutationHandler, true);
SimpleTest.finish();
}
runTest();
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, runTest);
</script>
</pre>

View File

@ -18,26 +18,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=367164
<script class="testbody" type="text/javascript">
/** Test for Bug 367164 **/
function test() {
var span = document.createElement("span");
var span = document.createElement("span");
var ins1 = false;
var ins2 = false;
var rem1 = false;
var rem2 = false;
var ins1 = false;
var ins2 = false;
var rem1 = false;
var rem2 = false;
span.addEventListener("DOMNodeInserted", function() { ins1 = true; }, true);
span.addEventListener("DOMNodeInserted", function() { ins2 = true; });
span.addEventListener("DOMNodeRemoved", function() { rem1 = true; }, true);
span.addEventListener("DOMNodeRemoved", function() { rem2 = true; });
span.addEventListener("DOMNodeInserted", function() { ins1 = true; }, true);
span.addEventListener("DOMNodeInserted", function() { ins2 = true; });
span.addEventListener("DOMNodeRemoved", function() { rem1 = true; }, true);
span.addEventListener("DOMNodeRemoved", function() { rem2 = true; });
$("content").appendChild(span);
$("content").removeChild(span);
$("content").appendChild(span);
$("content").removeChild(span);
is(ins1, true, "Capturing DOMNodeInserted listener");
is(ins2, true, "Bubbling DOMNodeInserted listener");
is(rem1, true, "Capturing DOMNodeRemoved listener");
is(rem2, true, "Bubbling DOMNodeRemoved listener");
SimpleTest.finish();
}
is(ins1, true, "Capturing DOMNodeInserted listener");
is(ins2, true, "Bubbling DOMNodeInserted listener");
is(rem1, true, "Capturing DOMNodeRemoved listener");
is(rem2, true, "Bubbling DOMNodeRemoved listener");
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>

View File

@ -34,12 +34,12 @@ function do_test()
ns.appendChild(nt);
dE.appendChild(ns);
ok(true, "Test is successful if we get here without crashing");
SimpleTest.finish();
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(do_test);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, do_test));
</script>
</pre>

View File

@ -41,9 +41,8 @@ function runTests() {
SimpleTest.finish();
}
addLoadEvent(runTests);
SimpleTest.waitForExplicitFinish();
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, runTests));
</script>
</pre>

View File

@ -52,18 +52,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=564863
<script class="testbody" type="text/javascript">
<![CDATA[
root = $('root');
div = root.children[0];
a = root.children[1];
svg = root.children[2].firstChild;
nsx = root.children[3];
function test() {
root = $('root');
div = root.children[0];
a = root.children[1];
svg = root.children[2].firstChild;
nsx = root.children[3];
var div_cs = getComputedStyle(div, "");
var a_cs = getComputedStyle(a, "");
var svg_cs = getComputedStyle(svg, "");
var nsx_cs = getComputedStyle(nsx, "");
var div_cs = getComputedStyle(div, "");
var a_cs = getComputedStyle(a, "");
var svg_cs = getComputedStyle(svg, "");
var nsx_cs = getComputedStyle(nsx, "");
function checkHasId(test) {
function checkHasId(test) {
// Check computed style first to avoid flushes from hiding problems
checkHasIdNoGEBI(test);
@ -71,9 +72,9 @@ function checkHasId(test) {
is($("a_id"), a, "a getElementById " + test);
is($("svg_id"), svg, "svg getElementById " + test);
is($("ns_id"), nsx, "ns getElementById " + test);
}
}
function checkHasIdNoGEBI(test) {
function checkHasIdNoGEBI(test) {
const connected = test != "removed node";
is(div_cs.color, connected ? "rgb(10, 10, 10)" : "", "div color " + test);
is(a_cs.color, connected ? "rgb(20, 20, 20)" : "", "a color " + test);
@ -84,9 +85,9 @@ function checkHasIdNoGEBI(test) {
is(a.id, "a_id", "a id " + test);
is(svg.id, "svg_id", "svg id " + test);
is (nsx.getAttribute("id"), "ns_id", "ns id " + test);
}
}
function checkHasNoId(removed, test) {
function checkHasNoId(removed, test) {
is(div_cs.color, "rgb(0, 0, 0)", "div color " + test);
is(a_cs.color, "rgb(0, 0, 0)", "a color " + test);
is(svg_cs.color, "rgb(0, 0, 0)", "svg color " + test);
@ -107,196 +108,200 @@ function checkHasNoId(removed, test) {
is($("a_id"), null, "a getElementById " + test);
is($("svg_id"), null, "svg getElementById " + test);
is($("ns_id"), null, "ns getElementById " + test);
}
}
// Check that dynamic modifications of attribute work
// Check that dynamic modifications of attribute work
checkHasId("in markup");
checkHasId("in markup");
div.id = "";
a.id = "";
svg.id = "";
nsx.setAttribute("id", "");
div.id = "";
a.id = "";
svg.id = "";
nsx.setAttribute("id", "");
checkHasNoId(false, "set to empty");
checkHasNoId(false, "set to empty");
div.id = "div_id";
a.id = "a_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
div.id = "div_id";
a.id = "a_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
checkHasId("set using .id");
checkHasId("set using .id");
div.setAttribute("id", "");
a.setAttribute("id", "");
svg.setAttribute("id", "");
nsx.setAttribute("id", "");
div.setAttribute("id", "");
a.setAttribute("id", "");
svg.setAttribute("id", "");
nsx.setAttribute("id", "");
checkHasNoId(false, "setAttribute to empty");
checkHasNoId(false, "setAttribute to empty");
div.id = "div_id";
a.id = "a_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
div.id = "div_id";
a.id = "a_id";
svg.id = "svg_id";
nsx.setAttribute("id", "ns_id");
checkHasId("set again using .id");
checkHasId("set again using .id");
div.removeAttribute("id");
a.removeAttribute("id");
svg.removeAttribute("id");
nsx.removeAttribute("id");
div.removeAttribute("id");
a.removeAttribute("id");
svg.removeAttribute("id");
nsx.removeAttribute("id");
checkHasNoId(true, "removed attribute");
checkHasNoId(true, "removed attribute");
div.setAttribute("id", "div_id");
a.setAttribute("id", "a_id");
svg.setAttribute("id", "svg_id");
nsx.setAttribute("id", "ns_id");
div.setAttribute("id", "div_id");
a.setAttribute("id", "a_id");
svg.setAttribute("id", "svg_id");
nsx.setAttribute("id", "ns_id");
checkHasId("set using setAttribute");
checkHasId("set using setAttribute");
t1 = document.createElement("div");
t1.id = "div_id";
t2 = document.createElement("a");
t2.id = "a_id";
t4 = document.createElementNS("http://www.w3.org/2000/svg", "g");
t4.id = "svg_id";
t5 = document.createElementNS("urn:namespace", "ns:x");
t5.setAttribute("id", "ns_id");
t1 = document.createElement("div");
t1.id = "div_id";
t2 = document.createElement("a");
t2.id = "a_id";
t4 = document.createElementNS("http://www.w3.org/2000/svg", "g");
t4.id = "svg_id";
t5 = document.createElementNS("urn:namespace", "ns:x");
t5.setAttribute("id", "ns_id");
// Check that inserting elements before/after existing work
// Check that inserting elements before/after existing work
function insertAfter(newChild, existing) {
function insertAfter(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing.nextSibling);
}
function insertBefore(newChild, existing) {
}
function insertBefore(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing);
}
function removeNode(child) {
}
function removeNode(child) {
child.remove();
}
}
insertAfter(t1, div);
insertAfter(t2, a);
insertAfter(t4, svg);
insertAfter(t5, nsx);
insertAfter(t1, div);
insertAfter(t2, a);
insertAfter(t4, svg);
insertAfter(t5, nsx);
checkHasId("inserted after");
checkHasId("inserted after");
insertBefore(t1, div);
insertBefore(t2, a);
insertBefore(t4, svg);
insertBefore(t5, nsx);
insertBefore(t1, div);
insertBefore(t2, a);
insertBefore(t4, svg);
insertBefore(t5, nsx);
checkHasIdNoGEBI("inserted before");
is($("div_id"), t1, "div getElementById inserted before");
is($("a_id"), t2, "a getElementById inserted before");
is($("svg_id"), t4, "svg getElementById inserted before");
is($("ns_id"), t5, "ns getElementById inserted before");
checkHasIdNoGEBI("inserted before");
is($("div_id"), t1, "div getElementById inserted before");
is($("a_id"), t2, "a getElementById inserted before");
is($("svg_id"), t4, "svg getElementById inserted before");
is($("ns_id"), t5, "ns getElementById inserted before");
t1.removeAttribute("id");
t2.removeAttribute("id");
t4.removeAttribute("id");
t5.removeAttribute("id");
t1.removeAttribute("id");
t2.removeAttribute("id");
t4.removeAttribute("id");
t5.removeAttribute("id");
checkHasId("removed tx attribute");
checkHasId("removed tx attribute");
t1.setAttribute("id", "div_id");
t2.setAttribute("id", "a_id");
t4.setAttribute("id", "svg_id");
t5.setAttribute("id", "ns_id");
t1.setAttribute("id", "div_id");
t2.setAttribute("id", "a_id");
t4.setAttribute("id", "svg_id");
t5.setAttribute("id", "ns_id");
checkHasIdNoGEBI("setAttribute before");
is($("div_id"), t1, "div getElementById setAttribute before");
is($("a_id"), t2, "a getElementById setAttribute before");
is($("svg_id"), t4, "svg getElementById setAttribute before");
is($("ns_id"), t5, "ns getElementById setAttribute before");
checkHasIdNoGEBI("setAttribute before");
is($("div_id"), t1, "div getElementById setAttribute before");
is($("a_id"), t2, "a getElementById setAttribute before");
is($("svg_id"), t4, "svg getElementById setAttribute before");
is($("ns_id"), t5, "ns getElementById setAttribute before");
removeNode(t1);
removeNode(t2);
removeNode(t4);
removeNode(t5);
removeNode(t1);
removeNode(t2);
removeNode(t4);
removeNode(t5);
checkHasId("removed temporaries");
checkHasId("removed temporaries");
removeNode(div);
removeNode(a);
removeNode(svg);
removeNode(nsx);
removeNode(div);
removeNode(a);
removeNode(svg);
removeNode(nsx);
checkHasIdNoGEBI("removed node");
checkHasIdNoGEBI("removed node");
// Check that removing an element during UnsetAttr works
is(div.id, "div_id", "div still has id set");
var mutateFired = false;
root.appendChild(div);
div.addEventListener("DOMAttrModified", function(e) {
// Check that removing an element during UnsetAttr works
is(div.id, "div_id", "div still has id set");
var mutateFired = false;
root.appendChild(div);
div.addEventListener("DOMAttrModified", function(e) {
is(e.target, div, "target is div");
is(div.id, "", "div no longer has id");
is(div.getAttribute("id"), null, "div no longer has id attr");
removeNode(div);
is(div.parentNode, null, "div was removed");
mutateFired = true;
}, {once: true});
div.removeAttribute("id");
ok(mutateFired, "mutation event fired");
}, {once: true});
div.removeAttribute("id");
ok(mutateFired, "mutation event fired");
// Check same for XML elements
is(nsx.getAttribute("id"), "ns_id", "nsx still has id set");
mutateFired = false;
root.appendChild(nsx);
nsx.addEventListener("DOMAttrModified", function(e) {
// Check same for XML elements
is(nsx.getAttribute("id"), "ns_id", "nsx still has id set");
mutateFired = false;
root.appendChild(nsx);
nsx.addEventListener("DOMAttrModified", function(e) {
is(e.target, nsx, "target is nsx");
is(nsx.getAttribute("id"), null, "nsx no longer has id attr");
removeNode(nsx);
is(nsx.parentNode, null, "nsx was removed");
mutateFired = true;
}, {once: true});
nsx.removeAttribute("id");
ok(mutateFired, "mutation event fired");
}, {once: true});
nsx.removeAttribute("id");
ok(mutateFired, "mutation event fired");
// Re-add the id inside a mutation event on a XML element
is($("ns_id"), null, "no nsx");
is($("ns2_id"), null, "no nsx");
nsx = document.createElementNS("urn:namespace", "ns:x");
nsx.setAttribute("id", "ns_id");
root.appendChild(nsx);
is($("ns_id"), nsx, "new nsx is set up");
mutateFired = false;
nsx.addEventListener("DOMAttrModified", function(e) {
// Re-add the id inside a mutation event on a XML element
is($("ns_id"), null, "no nsx");
is($("ns2_id"), null, "no nsx");
nsx = document.createElementNS("urn:namespace", "ns:x");
nsx.setAttribute("id", "ns_id");
root.appendChild(nsx);
is($("ns_id"), nsx, "new nsx is set up");
mutateFired = false;
nsx.addEventListener("DOMAttrModified", function(e) {
is(e.target, nsx, "target is nsx");
is(nsx.getAttribute("id"), null, "nsx no longer has id attr");
nsx.setAttribute("id", "other_id");
mutateFired = true;
}, {once: true});
nsx.removeAttribute("id");
ok(mutateFired, "mutation event fired");
is($("ns_id"), null, "ns_id was removed from table");
is($("other_id"), nsx, "other_id was added");
removeNode(nsx);
is($("other_id"), null, "other_id was removed");
}, {once: true});
nsx.removeAttribute("id");
ok(mutateFired, "mutation event fired");
is($("ns_id"), null, "ns_id was removed from table");
is($("other_id"), nsx, "other_id was added");
removeNode(nsx);
is($("other_id"), null, "other_id was removed");
// Re-add the id inside a mutation event on a HTML element
is($("div_id"), null, "no div");
div = document.createElement("div");
div.id = "div_id";
root.appendChild(div);
is($("div_id"), div, "new div is set up");
mutateFired = false;
div.addEventListener("DOMAttrModified", function(e) {
// Re-add the id inside a mutation event on a HTML element
is($("div_id"), null, "no div");
div = document.createElement("div");
div.id = "div_id";
root.appendChild(div);
is($("div_id"), div, "new div is set up");
mutateFired = false;
div.addEventListener("DOMAttrModified", function(e) {
is(e.target, div, "target is div");
is(div.getAttribute("id"), null, "div no longer has id attr");
is(div.id, "", "div no longer has id");
div.id = "other_div_id";
mutateFired = true;
}, {once: true});
div.removeAttribute("id");
ok(mutateFired, "mutation event fired");
is($("div_id"), null, "div_id was removed from table");
is($("other_div_id"), div, "other_div_id was added");
removeNode(div);
is($("other_div_id"), null, "other_div_id was removed");
}, {once: true});
div.removeAttribute("id");
ok(mutateFired, "mutation event fired");
is($("div_id"), null, "div_id was removed from table");
is($("other_div_id"), div, "other_div_id was added");
removeNode(div);
is($("other_div_id"), null, "other_div_id was removed");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
]]>
</script>

View File

@ -22,15 +22,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=588990
<pre id="test">
<script class="testbody" type="text/javascript">
root = $('root');
i1_1 = root.children[0];
i2_1 = root.children[1];
i2_2 = root.children[2];
i3_1 = root.children[3];
i3_2 = root.children[4];
i3_3 = root.children[5];
function test() {
root = $('root');
i1_1 = root.children[0];
i2_1 = root.children[1];
i2_2 = root.children[2];
i3_1 = root.children[3];
i3_2 = root.children[4];
i3_3 = root.children[5];
function checkHasName(test) {
function checkHasName(test) {
// Check name first to avoid flushes from hiding problems
checkHasNameNoDocProp(test);
@ -42,18 +43,18 @@ function checkHasName(test) {
is(document.n3[1], i3_2, "i3_2 doc.name " + test);
is(document.n3[2], i3_3, "i3_3 doc.name " + test);
is(document.n3.length, 3, "doc.name.length " + test);
}
}
function checkHasNameNoDocProp(test) {
function checkHasNameNoDocProp(test) {
is(i1_1.name, "n1", "i1_1 name " + test);
is(i2_1.name, "n2", "i2_1 name " + test);
is(i2_2.name, "n2", "i2_2 name " + test);
is(i3_1.name, "n3", "i3_1 name " + test);
is(i3_2.name, "n3", "i3_2 name " + test);
is(i3_3.name, "n3", "i3_3 name " + test);
}
}
function checkHasNoName(removed, test) {
function checkHasNoName(removed, test) {
is(i1_1.name, "", "i1_1 name " + test);
is(i2_1.name, "", "i2_1 name " + test);
is(i2_2.name, "", "i2_2 name " + test);
@ -72,259 +73,263 @@ function checkHasNoName(removed, test) {
is(document.n1, undefined, "doc.n1 " + test);
is(document.n2, undefined, "doc.n2 " + test);
is(document.n3, undefined, "doc.n3 " + test);
}
}
// Check that dynamic modifications of attribute work
// Check that dynamic modifications of attribute work
checkHasName("in markup");
checkHasName("in markup");
i1_1.name = "";
i2_1.name = "";
i2_2.name = "";
i3_1.name = "";
i3_2.name = "";
i3_3.name = "";
i1_1.name = "";
i2_1.name = "";
i2_2.name = "";
i3_1.name = "";
i3_2.name = "";
i3_3.name = "";
checkHasNoName(false, "set to empty");
checkHasNoName(false, "set to empty");
i1_1.name = "n1";
i2_1.name = "n2";
i2_2.name = "n2";
i3_1.name = "n3";
i3_2.name = "n3";
i3_3.name = "n3";
i1_1.name = "n1";
i2_1.name = "n2";
i2_2.name = "n2";
i3_1.name = "n3";
i3_2.name = "n3";
i3_3.name = "n3";
checkHasName("set using .name");
checkHasName("set using .name");
i1_1.setAttribute("name", "");
i2_1.setAttribute("name", "");
i2_2.setAttribute("name", "");
i3_1.setAttribute("name", "");
i3_2.setAttribute("name", "");
i3_3.setAttribute("name", "");
i1_1.setAttribute("name", "");
i2_1.setAttribute("name", "");
i2_2.setAttribute("name", "");
i3_1.setAttribute("name", "");
i3_2.setAttribute("name", "");
i3_3.setAttribute("name", "");
checkHasNoName(false, "setAttribute to empty");
checkHasNoName(false, "setAttribute to empty");
i1_1.name = "n1";
i2_1.name = "n2";
i2_2.name = "n2";
i3_1.name = "n3";
i3_2.name = "n3";
i3_3.name = "n3";
i1_1.name = "n1";
i2_1.name = "n2";
i2_2.name = "n2";
i3_1.name = "n3";
i3_2.name = "n3";
i3_3.name = "n3";
checkHasName("set again using .name");
checkHasName("set again using .name");
i1_1.removeAttribute("name");
i2_1.removeAttribute("name");
i2_2.removeAttribute("name");
i3_1.removeAttribute("name");
i3_2.removeAttribute("name");
i3_3.removeAttribute("name");
i1_1.removeAttribute("name");
i2_1.removeAttribute("name");
i2_2.removeAttribute("name");
i3_1.removeAttribute("name");
i3_2.removeAttribute("name");
i3_3.removeAttribute("name");
checkHasNoName(true, "removed attribute");
checkHasNoName(true, "removed attribute");
i1_1.setAttribute("name", "n1");
i2_1.setAttribute("name", "n2");
i2_2.setAttribute("name", "n2");
i3_1.setAttribute("name", "n3");
i3_2.setAttribute("name", "n3");
i3_3.setAttribute("name", "n3");
i1_1.setAttribute("name", "n1");
i2_1.setAttribute("name", "n2");
i2_2.setAttribute("name", "n2");
i3_1.setAttribute("name", "n3");
i3_2.setAttribute("name", "n3");
i3_3.setAttribute("name", "n3");
checkHasName("set using setAttribute");
checkHasName("set using setAttribute");
t1 = document.createElement("img");
t1.name = "n1";
t2 = document.createElement("img");
t2.name = "n2";
t3 = document.createElement("img");
t3.name = "n2";
t4 = document.createElement("img");
t4.name = "n3";
t5 = document.createElement("img");
t5.name = "n3";
t6 = document.createElement("img");
t6.name = "n3";
t1 = document.createElement("img");
t1.name = "n1";
t2 = document.createElement("img");
t2.name = "n2";
t3 = document.createElement("img");
t3.name = "n2";
t4 = document.createElement("img");
t4.name = "n3";
t5 = document.createElement("img");
t5.name = "n3";
t6 = document.createElement("img");
t6.name = "n3";
// Check that inserting elements before/after existing work
// Check that inserting elements before/after existing work
function insertAfter(newChild, existing) {
function insertAfter(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing.nextSibling);
}
function insertBefore(newChild, existing) {
}
function insertBefore(newChild, existing) {
existing.parentNode.insertBefore(newChild, existing);
}
function removeNode(child) {
}
function removeNode(child) {
child.remove();
}
}
insertAfter(t1, i1_1);
insertAfter(t2, i2_1);
insertAfter(t3, i2_2);
insertAfter(t4, i3_1);
insertAfter(t5, i3_2);
insertAfter(t6, i3_3);
insertAfter(t1, i1_1);
insertAfter(t2, i2_1);
insertAfter(t3, i2_2);
insertAfter(t4, i3_1);
insertAfter(t5, i3_2);
insertAfter(t6, i3_3);
checkHasNameNoDocProp("inserted after");
is(document.n1[0], i1_1, "i1_1 doc.name inserted after");
is(document.n1[1], t1, "t1 doc.name inserted after");
is(document.n1.length, 2, "doc.name1.length inserted after");
is(document.n2[0], i2_1, "i2_1 doc.name inserted after");
todo_is(document.n2[1], t2, "This is where t2 should show up. The elements in here should be in order-in-document rather than order-of-insertion");
is(document.n2[1], i2_2, "i2_2 doc.name inserted after");
is(document.n2[2], t2, "t2 doc.name inserted after");
is(document.n2[3], t3, "t3 doc.name inserted after");
is(document.n2.length, 4, "doc.name2.length inserted after");
is(document.n3[0], i3_1, "i3_1 doc.name inserted after");
is(document.n3[1], i3_2, "i3_3 doc.name inserted after");
is(document.n3[2], i3_3, "i3_2 doc.name inserted after");
is(document.n3[3], t4, "t4 doc.name inserted after");
is(document.n3[4], t5, "t5 doc.name inserted after");
is(document.n3[5], t6, "t6 doc.name inserted after");
is(document.n3.length, 6, "doc.name3.length inserted after");
checkHasNameNoDocProp("inserted after");
is(document.n1[0], i1_1, "i1_1 doc.name inserted after");
is(document.n1[1], t1, "t1 doc.name inserted after");
is(document.n1.length, 2, "doc.name1.length inserted after");
is(document.n2[0], i2_1, "i2_1 doc.name inserted after");
todo_is(document.n2[1], t2, "This is where t2 should show up. The elements in here should be in order-in-document rather than order-of-insertion");
is(document.n2[1], i2_2, "i2_2 doc.name inserted after");
is(document.n2[2], t2, "t2 doc.name inserted after");
is(document.n2[3], t3, "t3 doc.name inserted after");
is(document.n2.length, 4, "doc.name2.length inserted after");
is(document.n3[0], i3_1, "i3_1 doc.name inserted after");
is(document.n3[1], i3_2, "i3_3 doc.name inserted after");
is(document.n3[2], i3_3, "i3_2 doc.name inserted after");
is(document.n3[3], t4, "t4 doc.name inserted after");
is(document.n3[4], t5, "t5 doc.name inserted after");
is(document.n3[5], t6, "t6 doc.name inserted after");
is(document.n3.length, 6, "doc.name3.length inserted after");
insertBefore(t1, i1_1);
insertBefore(t2, i2_1);
insertBefore(t3, i2_2);
insertBefore(t4, i3_1);
insertBefore(t5, i3_2);
insertBefore(t6, i3_3);
insertBefore(t1, i1_1);
insertBefore(t2, i2_1);
insertBefore(t3, i2_2);
insertBefore(t4, i3_1);
insertBefore(t5, i3_2);
insertBefore(t6, i3_3);
checkHasNameNoDocProp("inserted before");
is(document.n1[0], i1_1, "i1_1 doc.name inserted before");
is(document.n1[1], t1, "t1 doc.name inserted before");
is(document.n1.length, 2, "doc.name1.length inserted before");
is(document.n2[0], i2_1, "i2_1 doc.name inserted before");
is(document.n2[1], i2_2, "i2_2 doc.name inserted before");
is(document.n2[2], t2, "t2 doc.name inserted before");
is(document.n2[3], t3, "t3 doc.name inserted before");
is(document.n2.length, 4, "doc.name2.length inserted before");
is(document.n3[0], i3_1, "i3_1 doc.name inserted before");
is(document.n3[1], i3_2, "i3_3 doc.name inserted before");
is(document.n3[2], i3_3, "i3_2 doc.name inserted before");
is(document.n3[3], t4, "t4 doc.name inserted before");
is(document.n3[4], t5, "t5 doc.name inserted before");
is(document.n3[5], t6, "t6 doc.name inserted before");
is(document.n3.length, 6, "doc.name3.length inserted before");
checkHasNameNoDocProp("inserted before");
is(document.n1[0], i1_1, "i1_1 doc.name inserted before");
is(document.n1[1], t1, "t1 doc.name inserted before");
is(document.n1.length, 2, "doc.name1.length inserted before");
is(document.n2[0], i2_1, "i2_1 doc.name inserted before");
is(document.n2[1], i2_2, "i2_2 doc.name inserted before");
is(document.n2[2], t2, "t2 doc.name inserted before");
is(document.n2[3], t3, "t3 doc.name inserted before");
is(document.n2.length, 4, "doc.name2.length inserted before");
is(document.n3[0], i3_1, "i3_1 doc.name inserted before");
is(document.n3[1], i3_2, "i3_3 doc.name inserted before");
is(document.n3[2], i3_3, "i3_2 doc.name inserted before");
is(document.n3[3], t4, "t4 doc.name inserted before");
is(document.n3[4], t5, "t5 doc.name inserted before");
is(document.n3[5], t6, "t6 doc.name inserted before");
is(document.n3.length, 6, "doc.name3.length inserted before");
t1.removeAttribute("name");
t2.removeAttribute("name");
t3.removeAttribute("name");
t4.removeAttribute("name");
t5.removeAttribute("name");
t6.removeAttribute("name");
t1.removeAttribute("name");
t2.removeAttribute("name");
t3.removeAttribute("name");
t4.removeAttribute("name");
t5.removeAttribute("name");
t6.removeAttribute("name");
checkHasName("removed tx attribute");
checkHasName("removed tx attribute");
t1.setAttribute("name", "n1");
t2.setAttribute("name", "n2");
t3.setAttribute("name", "n2");
t4.setAttribute("name", "n3");
t5.setAttribute("name", "n3");
t6.setAttribute("name", "n3");
t1.setAttribute("name", "n1");
t2.setAttribute("name", "n2");
t3.setAttribute("name", "n2");
t4.setAttribute("name", "n3");
t5.setAttribute("name", "n3");
t6.setAttribute("name", "n3");
checkHasNameNoDocProp("inserted before");
is(document.n1[0], i1_1, "i1_1 doc.name inserted before");
is(document.n1[1], t1, "t1 doc.name inserted before");
is(document.n1.length, 2, "doc.name1.length inserted before");
is(document.n2[0], i2_1, "i2_1 doc.name inserted before");
is(document.n2[1], i2_2, "i2_2 doc.name inserted before");
is(document.n2[2], t2, "t2 doc.name inserted before");
is(document.n2[3], t3, "t3 doc.name inserted before");
is(document.n2.length, 4, "doc.name2.length inserted before");
is(document.n3[0], i3_1, "i3_1 doc.name inserted before");
is(document.n3[1], i3_2, "i3_3 doc.name inserted before");
is(document.n3[2], i3_3, "i3_2 doc.name inserted before");
is(document.n3[3], t4, "t4 doc.name inserted before");
is(document.n3[4], t5, "t5 doc.name inserted before");
is(document.n3[5], t6, "t6 doc.name inserted before");
is(document.n3.length, 6, "doc.name3.length inserted before");
checkHasNameNoDocProp("inserted before");
is(document.n1[0], i1_1, "i1_1 doc.name inserted before");
is(document.n1[1], t1, "t1 doc.name inserted before");
is(document.n1.length, 2, "doc.name1.length inserted before");
is(document.n2[0], i2_1, "i2_1 doc.name inserted before");
is(document.n2[1], i2_2, "i2_2 doc.name inserted before");
is(document.n2[2], t2, "t2 doc.name inserted before");
is(document.n2[3], t3, "t3 doc.name inserted before");
is(document.n2.length, 4, "doc.name2.length inserted before");
is(document.n3[0], i3_1, "i3_1 doc.name inserted before");
is(document.n3[1], i3_2, "i3_3 doc.name inserted before");
is(document.n3[2], i3_3, "i3_2 doc.name inserted before");
is(document.n3[3], t4, "t4 doc.name inserted before");
is(document.n3[4], t5, "t5 doc.name inserted before");
is(document.n3[5], t6, "t6 doc.name inserted before");
is(document.n3.length, 6, "doc.name3.length inserted before");
removeNode(t1);
removeNode(t2);
removeNode(t3);
removeNode(t4);
removeNode(t5);
removeNode(t6);
removeNode(t1);
removeNode(t2);
removeNode(t3);
removeNode(t4);
removeNode(t5);
removeNode(t6);
checkHasName("removed temporaries");
checkHasName("removed temporaries");
removeNode(i1_1);
removeNode(i2_1);
removeNode(i2_2);
removeNode(i3_1);
removeNode(i3_2);
removeNode(i3_3);
removeNode(i1_1);
removeNode(i2_1);
removeNode(i2_2);
removeNode(i3_1);
removeNode(i3_2);
removeNode(i3_3);
checkHasNameNoDocProp("removed node");
checkHasNameNoDocProp("removed node");
// Check that removing an element during UnsetAttr works
is(i1_1.name, "n1", "i1_1 has name set");
var mutateFired = false;
root.appendChild(i1_1);
i1_1.addEventListener("DOMAttrModified", function(e) {
// Check that removing an element during UnsetAttr works
is(i1_1.name, "n1", "i1_1 has name set");
var mutateFired = false;
root.appendChild(i1_1);
i1_1.addEventListener("DOMAttrModified", function(e) {
is(e.target, i1_1, "target is i1_1");
is(i1_1.name, "", "i1_1 no longer has name");
is(i1_1.getAttribute("name"), null, "i1_1 no longer has name attr");
removeNode(i1_1);
is(i1_1.parentNode, null, "i1_1 was removed");
mutateFired = true;
}, {once: true});
i1_1.removeAttribute("name");
ok(mutateFired, "mutation event fired");
SpecialPowers.gc();
}, {once: true});
i1_1.removeAttribute("name");
ok(mutateFired, "mutation event fired");
SpecialPowers.gc();
// Check that removing an element during SetAttr works
i2_1.name = "";
mutateFired = false;
root.appendChild(i2_1);
i2_1.addEventListener("DOMAttrModified", function(e) {
// Check that removing an element during SetAttr works
i2_1.name = "";
mutateFired = false;
root.appendChild(i2_1);
i2_1.addEventListener("DOMAttrModified", function(e) {
is(e.target, i2_1, "target is i2_1");
is(i2_1.name, "n2", "i2_1 no longer has name");
is(i2_1.getAttribute("name"), "n2", "i2_1 no longer has name attr");
removeNode(i2_1);
is(i2_1.parentNode, null, "i2_1 was removed");
mutateFired = true;
}, {once: true});
i2_1.name = "n2";
ok(mutateFired, "mutation event fired");
SpecialPowers.gc();
}, {once: true});
i2_1.name = "n2";
ok(mutateFired, "mutation event fired");
SpecialPowers.gc();
// Re-add the name inside a mutation event on a HTML element
is(i2_2.name, "n2", "i2_2 has name set");
root.appendChild(i2_2);
mutateFired = false;
root.appendChild(i2_2);
i2_2.addEventListener("DOMAttrModified", function(e) {
// Re-add the name inside a mutation event on a HTML element
is(i2_2.name, "n2", "i2_2 has name set");
root.appendChild(i2_2);
mutateFired = false;
root.appendChild(i2_2);
i2_2.addEventListener("DOMAttrModified", function(e) {
is(e.target, i2_2, "target is i2_2");
is(i2_2.name, "", "i2_2 no longer has name");
is(i2_2.getAttribute("name"), "", "i2_2 has empty name attr");
i2_2.name = "n2";
mutateFired = true;
}, {once: true});
i2_2.name = "";
ok(mutateFired, "mutation event fired");
is(document.n2, i2_2, "named was readded during mutation");
removeNode(i2_2);
SpecialPowers.gc();
}, {once: true});
i2_2.name = "";
ok(mutateFired, "mutation event fired");
is(document.n2, i2_2, "named was readded during mutation");
removeNode(i2_2);
SpecialPowers.gc();
// Re-remove the name inside a mutation event on a HTML element
i3_1.name = "";
root.appendChild(i3_1);
mutateFired = false;
root.appendChild(i3_1);
i3_1.addEventListener("DOMAttrModified", function(e) {
// Re-remove the name inside a mutation event on a HTML element
i3_1.name = "";
root.appendChild(i3_1);
mutateFired = false;
root.appendChild(i3_1);
i3_1.addEventListener("DOMAttrModified", function(e) {
is(e.target, i3_1, "target is i3_1");
is(i3_1.name, "n3", "i3_1 no longer has name");
is(i3_1.getAttribute("name"), "n3", "i3_1 has empty name attr");
i3_1.removeAttribute("name");
mutateFired = true;
}, {once: true});
i3_1.name = "n3";
ok(mutateFired, "mutation event fired");
is(document.n3, undefined, "named was readded during mutation");
removeNode(i3_1);
SpecialPowers.gc();
}, {once: true});
i3_1.name = "n3";
ok(mutateFired, "mutation event fired");
is(document.n3, undefined, "named was readded during mutation");
removeNode(i3_1);
SpecialPowers.gc();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</pre>

View File

@ -40,7 +40,7 @@ function boom()
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(boom);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, boom));
</script>
</pre>

View File

@ -9,7 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=641821
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="runTest()">
<body onload="SpecialPowers.pushPrefEnv({'set': [['dom.mutation_events.enabled', true]]}, runTest)">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641821">Mozilla Bug 641821</a>
<p id="display"></p>
<div id="content" style="display: none">

View File

@ -17,7 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1127588
SimpleTest.waitForExplicitFinish();
window.onload = function () {
window.onload = function() {
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
}
function test() {
let insertedEventCount = 0;
let insertedListener = function() {
insertedEventCount++;

View File

@ -90,11 +90,11 @@ function doTest() {
subtree.normalize();
is(subtreeModifiedCount, 1,
"Calling normalize() should have dispatched a DOMSubtreeModified event");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
addLoadEvent(SimpleTest.finish);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, doTest));
</script>
</pre>

View File

@ -126,7 +126,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=328885
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged");
addLoadEvent(doTest);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, doTest));
</script>
</pre>

View File

@ -38,11 +38,12 @@ function doTest() {
i2.contentDocument.adoptNode(pre);
i2.contentDocument.body.appendChild(pre);
ok(eventCounter == 2, "DOMNodeInserted should have been dispatched in the new document");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
addLoadEvent(SimpleTest.finish);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, doTest));
</script>
</pre>

View File

@ -17,7 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=650493
<pre id="test">
<script class="testbody" type="text/javascript">
function getNodes() {
function test() {
function getNodes() {
var walker = document.createTreeWalker($('content'), NodeFilter.SHOW_ALL, null);
var nodes = [];
do {
@ -25,25 +26,25 @@ function getNodes() {
} while(walker.nextNode());
return nodes;
}
}
function check() {
function check() {
var current = getNodes();
is(nodes.length, current.length, "length after " + testName);
nodes.forEach(function(val, index) {
ok(current.indexOf(val) > -1, "nodes[" + index + "] (" + val + ") shouldn't exist after " + testName);
});
}
}
var nodes = getNodes();
var testName = "empty";
var mutateCount = 0;
var nodes = getNodes();
var testName = "empty";
var mutateCount = 0;
check();
check();
// Set up listeners
root = $('content');
root.addEventListener("DOMNodeInserted", function(e) {
// Set up listeners
root = $('content');
root.addEventListener("DOMNodeInserted", function(e) {
mutateCount++;
is(e.isTrusted, true, "untrusted mutation event");
var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null);
@ -51,8 +52,8 @@ root.addEventListener("DOMNodeInserted", function(e) {
is(nodes.indexOf(w.currentNode), -1, "already have inserted node (" + w.currentNode + ") when " + testName);
nodes.push(w.currentNode);
} while(w.nextNode());
});
root.addEventListener("DOMNodeRemoved", function(e) {
});
root.addEventListener("DOMNodeRemoved", function(e) {
mutateCount++;
is(e.isTrusted, true, "untrusted mutation event");
var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null);
@ -61,153 +62,158 @@ root.addEventListener("DOMNodeRemoved", function(e) {
ok(index != -1, "missing removed node (" + w.currentNode + ") when " + testName);
nodes.splice(index, 1);
} while(w.nextNode());
});
});
testName = "text-only innerHTML";
root.innerHTML = "hello world";
check();
testName = "text-only innerHTML";
root.innerHTML = "hello world";
check();
testName = "innerHTML with <b>";
root.innerHTML = "<b>bold</b> world";
check();
testName = "innerHTML with <b>";
root.innerHTML = "<b>bold</b> world";
check();
testName = "complex innerHTML";
root.innerHTML = "<b>b<span>old</span></b> <strong>world";
check();
testName = "complex innerHTML";
root.innerHTML = "<b>b<span>old</span></b> <strong>world";
check();
testName = "replacing using .textContent";
root.textContent = "i'm just a plain text minding my own business";
check();
testName = "replacing using .textContent";
root.textContent = "i'm just a plain text minding my own business";
check();
testName = "clearing using .textContent";
root.textContent = "";
check();
testName = "clearing using .textContent";
root.textContent = "";
check();
testName = "inserting using .textContent";
root.textContent = "i'm new text!!";
check();
testName = "inserting using .textContent";
root.textContent = "i'm new text!!";
check();
testName = "inserting using .textContent";
root.textContent = "i'm new text!!";
check();
testName = "inserting using .textContent";
root.textContent = "i'm new text!!";
check();
testName = "preparing to normalize";
root.innerHTML = "<u><b>foo</b></u> ";
var u = root.firstChild;
is(u.nodeName, "U", "got the right node");
var b = u.firstChild;
is(b.nodeName, "B", "got the right node");
b.insertBefore(document.createTextNode(""), b.firstChild);
b.insertBefore(document.createTextNode(""), b.firstChild);
b.appendChild(document.createTextNode(""));
b.appendChild(document.createTextNode("hello"));
b.appendChild(document.createTextNode("world"));
u.appendChild(document.createTextNode("foo"));
u.appendChild(document.createTextNode(""));
u.appendChild(document.createTextNode("bar"));
check();
testName = "preparing to normalize";
root.innerHTML = "<u><b>foo</b></u> ";
var u = root.firstChild;
is(u.nodeName, "U", "got the right node");
var b = u.firstChild;
is(b.nodeName, "B", "got the right node");
b.insertBefore(document.createTextNode(""), b.firstChild);
b.insertBefore(document.createTextNode(""), b.firstChild);
b.appendChild(document.createTextNode(""));
b.appendChild(document.createTextNode("hello"));
b.appendChild(document.createTextNode("world"));
u.appendChild(document.createTextNode("foo"));
u.appendChild(document.createTextNode(""));
u.appendChild(document.createTextNode("bar"));
check();
testName = "normalizing";
root.normalize();
check();
testName = "normalizing";
root.normalize();
check();
testName = "self replace firstChild";
mutateCount = 0;
root.replaceChild(root.firstChild, root.firstChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self replace firstChild";
mutateCount = 0;
root.replaceChild(root.firstChild, root.firstChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self replace second child";
mutateCount = 0;
root.replaceChild(root.firstChild.nextSibling, root.firstChild.nextSibling);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self replace second child";
mutateCount = 0;
root.replaceChild(root.firstChild.nextSibling, root.firstChild.nextSibling);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self replace lastChild";
mutateCount = 0;
root.replaceChild(root.lastChild, root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self replace lastChild";
mutateCount = 0;
root.replaceChild(root.lastChild, root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore firstChild";
mutateCount = 0;
root.insertBefore(root.firstChild, root.firstChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore firstChild";
mutateCount = 0;
root.insertBefore(root.firstChild, root.firstChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore second child";
mutateCount = 0;
root.insertBefore(root.firstChild.nextSibling, root.firstChild.nextSibling);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore second child";
mutateCount = 0;
root.insertBefore(root.firstChild.nextSibling, root.firstChild.nextSibling);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore lastChild";
mutateCount = 0;
root.insertBefore(root.lastChild, root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "self insertBefore lastChild";
mutateCount = 0;
root.insertBefore(root.lastChild, root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "appendChild last";
mutateCount = 0;
root.appendChild(root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "appendChild last";
mutateCount = 0;
root.appendChild(root.lastChild);
check();
is(mutateCount, 2, "should remove and reinsert " + testName);
testName = "prepare script/style";
script = document.createElement("script");
script.appendChild(document.createTextNode("void(0);"));
root.appendChild(script);
style = document.createElement("style");
root.appendChild(style);
check();
testName = "prepare script/style";
script = document.createElement("script");
script.appendChild(document.createTextNode("void(0);"));
root.appendChild(script);
style = document.createElement("style");
root.appendChild(style);
check();
testName = "set something in script";
script.text = "something";
check();
testName = "set something in script";
script.text = "something";
check();
testName = "set something in style";
style.innerHTML = "something { dislay: none; }";
check();
testName = "set something in style";
style.innerHTML = "something { dislay: none; }";
check();
testName = "moving style";
root.insertBefore(style, root.firstChild);
check();
testName = "moving style";
root.insertBefore(style, root.firstChild);
check();
testName = "replacing script";
root.replaceChild(b, script);
check();
testName = "replacing script";
root.replaceChild(b, script);
check();
testName = "doc-fragment insert in the middle";
frag = document.createDocumentFragment();
frag.addEventListener("DOMNodeRemoved", function(e) {
testName = "doc-fragment insert in the middle";
frag = document.createDocumentFragment();
frag.addEventListener("DOMNodeRemoved", function(e) {
var index = children.indexOf(e.target);
ok(index != -1, "unknown child removed from fragment");
children.splice(index, 1);
});
var children = [];
children.push(document.createTextNode("foo"));
children.push(document.createTextNode("bar"));
children.push(document.createElement("span"));
children.push(document.createElement("b"));
children[2].appendChild(document.createElement("i"));
children.forEach(function(child) { frag.appendChild(child); });
ok(root.firstChild, "need to have children in order to test inserting before end");
root.replaceChild(frag, root.firstChild);
check();
is(children.length, 0, "should have received DOMNodeRemoved for all frag children when inserting");
is(frag.childNodes.length, 0, "fragment should be empty when inserting");
});
var children = [];
children.push(document.createTextNode("foo"));
children.push(document.createTextNode("bar"));
children.push(document.createElement("span"));
children.push(document.createElement("b"));
children[2].appendChild(document.createElement("i"));
children.forEach(function(child) { frag.appendChild(child); });
ok(root.firstChild, "need to have children in order to test inserting before end");
root.replaceChild(frag, root.firstChild);
check();
is(children.length, 0, "should have received DOMNodeRemoved for all frag children when inserting");
is(frag.childNodes.length, 0, "fragment should be empty when inserting");
testName = "doc-fragment append at the end";
children.push(document.createTextNode("foo"));
children.push(document.createTextNode("bar"));
children.push(document.createElement("span"));
children.push(document.createElement("b"));
children[2].appendChild(document.createElement("i"));
children.forEach(function(child) { frag.appendChild(child); });
root.appendChild(frag);
check();
is(children.length, 0, "should have received DOMNodeRemoved for all frag children when appending");
is(frag.childNodes.length, 0, "fragment should be empty when appending");
testName = "doc-fragment append at the end";
children.push(document.createTextNode("foo"));
children.push(document.createTextNode("bar"));
children.push(document.createElement("span"));
children.push(document.createElement("b"));
children[2].appendChild(document.createElement("i"));
children.forEach(function(child) { frag.appendChild(child); });
root.appendChild(frag);
check();
is(children.length, 0, "should have received DOMNodeRemoved for all frag children when appending");
is(frag.childNodes.length, 0, "fragment should be empty when appending");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
</script>
</body>

View File

@ -50,11 +50,11 @@ function test() {
testElementType("option");
testElementType("optgroup");
testElementType("button");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(test);
addLoadEvent(SimpleTest.finish);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test));
</script>
</pre>
</body>

View File

@ -35,11 +35,11 @@ function testSelectedIndex()
var selectElement = document.getElementsByTagName("select")[0];
is(selectElement.selectedIndex, -1, "Wrong selected index!");
is(selectElement.length, 0, "Select shouldn't have any options!");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(testSelectedIndex);
addLoadEvent(SimpleTest.finish);
addLoadEvent(() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, testSelectedIndex));
</script>

View File

@ -2,7 +2,8 @@
<html class="reftest-wait">
<head>
<script>
document.addEventListener("DOMContentLoaded", () => {
document.addEventListener("DOMContentLoaded", () =>
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, () => {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const set = document.createElementNS("http://www.w3.org/2000/svg", "set");
svg.appendChild(set);
@ -16,7 +17,8 @@ document.addEventListener("DOMContentLoaded", () => {
e.originalTarget.setCurrentTime(1.050520798894502e38);
document.documentElement.removeAttribute("class");
});
});
})
);
</script>
</head>
</html>

View File

@ -149,7 +149,8 @@ function run_tests() {
SimpleTest.finish();
}
window.addEventListener("load", run_tests);
window.addEventListener("load",
() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, run_tests));
]]>
</script>

View File

@ -65,7 +65,8 @@ function run_tests() {
SimpleTest.finish();
}
window.addEventListener("load", run_tests);
window.addEventListener("load",
() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, run_tests));
]]>
</script>

View File

@ -120,7 +120,8 @@ function run_tests() {
SimpleTest.finish();
}
window.addEventListener("load", run_tests);
window.addEventListener("load",
() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, run_tests));
]]>
</script>

View File

@ -452,7 +452,8 @@ function testMutationEvents(g) {
eventChecker.finish();
}
window.addEventListener("load", main);
window.addEventListener("load",
() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, main));
]]>
</script>

View File

@ -1363,7 +1363,8 @@ function run_tests() {
SimpleTest.finish();
}
window.addEventListener("load", run_tests);
window.addEventListener("load",
() => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, run_tests));
]]>
</script>

View File

@ -250,7 +250,8 @@ function runTests() {
SimpleTest.finish();
}
window.addEventListener("load", runTests);
window.addEventListener("load", () =>
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, runTests));
</script>
</pre>
</body>

View File

@ -13,6 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1870055
SimpleTest.waitForExplicitFinish();
document.addEventListener('DOMContentLoaded', async () => {
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, () => {
const node = document.createElement('div');
document.documentElement.appendChild(node);
document.addEventListener('DOMSubtreeModified', (e) => {
@ -21,5 +22,6 @@ document.addEventListener('DOMContentLoaded', async () => {
SimpleTest.finish();
}, { once: true });
node.setHTMLUnsafe('a');
});
});
</script>

View File

@ -20,7 +20,7 @@ function onLoad() {
}
</script>
</head>
<body onload="onLoad()">
<body onload="SpecialPowers.pushPrefEnv({'set': [['dom.mutation_events.enabled', true]]}, onLoad)">
<table></table>
<style></style>
<spacer contenteditable>

View File

@ -24,6 +24,6 @@ function init()
</head>
<body onload="init()"><div id="ww"><input type="text" value="inputtext" id="inp">moretext</div></body>
<body onload="SpecialPowers.pushPrefEnv({'set': [['dom.mutation_events.enabled', true]]}, init)"><div id="ww"><input type="text" value="inputtext" id="inp">moretext</div></body>
</html>

View File

@ -3246,6 +3246,12 @@
value: true
mirror: always
# Whether to dispatch mutation events.
- name: dom.mutation_events.enabled
type: bool
value: true
mirror: always
# Limit of location change caused by content scripts in a time span per
# BrowsingContext. This includes calls to History and Location APIs.
- name: dom.navigation.locationChangeRateLimit.count

View File

@ -794,6 +794,10 @@ pref("dom.disable_window_move_resize", false);
pref("dom.allow_scripts_to_close_windows", false);
// List of urls for which mutation events are enabled even if mutation events
// in general are disabled. See nsContentUtils::IsURIInPrefList.
pref("dom.mutation_events.forceEnable", "");
pref("dom.popup_allowed_events", "change click dblclick auxclick mousedown mouseup pointerdown pointerup notificationclick reset submit touchend contextmenu");
pref("dom.serviceWorkers.disable_open_click_delay", 1000);

View File

@ -15,8 +15,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=613662
SimpleTest.expectAssertions(1);
/** Test for Bug 613662 **/
function testPositions(node) {
function test() {
function testPositions(node) {
node.insertAdjacentHTML("beforeBegin", "\u003Cscript>ok(false, 'script should not have run');\u003C/script><i></i>");
is(node.previousSibling.localName, "i", "Should have had <i> as previous sibling");
node.insertAdjacentHTML("Afterbegin", "<b></b>\u003Cscript>ok(false, 'script should not have run');\u003C/script>");
@ -25,58 +25,58 @@ function testPositions(node) {
is(node.lastChild.localName, "u", "Should have had <u> as last child");
node.insertAdjacentHTML("afterend", "<a></a>\u003Cscript>ok(false, 'script should not have run');\u003C/script>");
is(node.nextSibling.localName, "a", "Should have had <a> as next sibling");
}
}
var content = document.getElementById("content");
testPositions(content); // without next sibling
testPositions(content); // test again when there's next sibling
var content = document.getElementById("content");
testPositions(content); // without next sibling
testPositions(content); // test again when there's next sibling
try {
try {
content.insertAdjacentHTML("bar", "foo");
ok(false, "insertAdjacentHTML should have thrown");
} catch (e) {
} catch (e) {
is(e.name, "SyntaxError", "insertAdjacentHTML should throw SyntaxError");
is(e.code, 12, "insertAdjacentHTML should throw SYNTAX_ERR");
}
}
var parent = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
var child = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
var parent = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
var child = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
try {
try {
child.insertAdjacentHTML("Beforebegin", "foo");
ok(false, "insertAdjacentHTML should have thrown");
} catch (e) {
} catch (e) {
is(e.name, "NoModificationAllowedError", "insertAdjacentHTML should throw NoModificationAllowedError");
is(e.code, 7, "insertAdjacentHTML should throw NO_MODIFICATION_ALLOWED_ERR");
}
}
try {
try {
child.insertAdjacentHTML("AfterEnd", "foo");
ok(false, "insertAdjacentHTML should have thrown");
} catch (e) {
} catch (e) {
is(e.name, "NoModificationAllowedError", "insertAdjacentHTML should throw NoModificationAllowedError");
is(e.code, 7, "insertAdjacentHTML should throw NO_MODIFICATION_ALLOWED_ERR");
}
}
child.insertAdjacentHTML("afterBegin", "foo"); // mustn't throw
child.insertAdjacentHTML("beforeend", "foo"); // mustn't throw
child.insertAdjacentHTML("afterBegin", "foo"); // mustn't throw
child.insertAdjacentHTML("beforeend", "foo"); // mustn't throw
parent.appendChild(child);
testPositions(child); // node not in tree but has parent
parent.appendChild(child);
testPositions(child); // node not in tree but has parent
content.appendChild(parent); // must not run scripts
content.appendChild(parent); // must not run scripts
try {
try {
document.documentElement.insertAdjacentHTML("afterend", "<div></div>");
ok(false, "insertAdjacentHTML should have thrown");
} catch (e) {
} catch (e) {
is(e.name, "NoModificationAllowedError", "insertAdjacentHTML should throw NoModificationAllowedError");
is(e.code, 7, "insertAdjacentHTML should throw NO_MODIFICATION_ALLOWED_ERR");
}
}
var content2 = document.getElementById("content2");
var content2 = document.getElementById("content2");
var events = [
var events = [
[ "DOMNodeInserted", document.body ],
[ "DOMNodeInserted", document.body ],
[ "DOMSubtreeModified", null ],
@ -101,36 +101,39 @@ var events = [
[ "DOMNodeInserted", document.body ],
[ "DOMNodeInserted", document.body ],
[ "DOMSubtreeModified", null ],
];
];
function mutationEventListener(evt) {
function mutationEventListener(evt) {
var expected = events.shift();
is(evt.type, expected[0], "Unexpected mutation type");
is(evt.relatedNode, expected[1], "Unexpected related node");
}
}
document.addEventListener("DOMSubtreeModified", mutationEventListener);
document.addEventListener("DOMNodeInserted", mutationEventListener);
document.addEventListener("DOMNodeRemoved", mutationEventListener);
document.addEventListener("DOMNodeRemovedFromDocument", mutationEventListener);
document.addEventListener("DOMNodeInsertedIntoDocument", mutationEventListener);
document.addEventListener("DOMAttrModified", mutationEventListener);
document.addEventListener("DOMCharacterDataModified", mutationEventListener);
document.addEventListener("DOMSubtreeModified", mutationEventListener);
document.addEventListener("DOMNodeInserted", mutationEventListener);
document.addEventListener("DOMNodeRemoved", mutationEventListener);
document.addEventListener("DOMNodeRemovedFromDocument", mutationEventListener);
document.addEventListener("DOMNodeInsertedIntoDocument", mutationEventListener);
document.addEventListener("DOMAttrModified", mutationEventListener);
document.addEventListener("DOMCharacterDataModified", mutationEventListener);
testPositions(content2); // without next sibling
testPositions(content2); // test again when there's next sibling
testPositions(content2); // without next sibling
testPositions(content2); // test again when there's next sibling
is(events.length, 0, "Not all expected events fired.");
is(events.length, 0, "Not all expected events fired.");
// XML-only:
try {
// XML-only:
try {
content.insertAdjacentHTML("beforeend", "<p>");
ok(false, "insertAdjacentHTML should have thrown");
} catch (e) {
} catch (e) {
is(e.name, "SyntaxError", "insertAdjacentHTML should throw SyntaxError");
is(e.code, 12, "insertAdjacentHTML should throw SYNTAX_ERR");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, test);
]]></script>
</pre>
</body>

View File

@ -1,3 +1,5 @@
prefs: [dom.mutation_events.enabled: true]
[move-inserted-node-from-DOMNodeInserted-during-exec-command-insertHTML.html]
expected:
if (os == "android") and fission: [TIMEOUT, OK]

View File

@ -1,3 +1,5 @@
prefs: [dom.mutation_events.enabled: true]
[042.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]

View File

@ -1,3 +1,5 @@
prefs: [dom.mutation_events.enabled: true]
[043.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]

View File

@ -1,3 +1,5 @@
prefs: [dom.mutation_events.enabled: true]
[054.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]

View File

@ -1,3 +1,5 @@
prefs: [dom.mutation_events.enabled: true]
[055.html]
expected:
if fission and (os == "android"): [OK, TIMEOUT]

View File

@ -0,0 +1 @@
prefs: [dom.mutation_events.enabled: true]

View File

@ -0,0 +1 @@
prefs: [dom.mutation_events.enabled: true]

View File

@ -0,0 +1 @@
prefs: [dom.mutation_events.enabled: true]

View File

@ -736,7 +736,8 @@ function init() {
["mousewheel.with_meta.action", 0],
["mousewheel.with_meta.action.override_x", -1],
["dom.events.textevent.enabled", true],
["layout.overflow-underflow.content.enabled", true]]}, runNextTest);
["layout.overflow-underflow.content.enabled", true],
["dom.mutation_events.enabled", true]]}, runNextTest);
}
function finish() {

View File

@ -10975,6 +10975,7 @@ async function runTest()
set: [
["dom.events.textevent.enabled", true],
["test.ime_content_observer.assert_invalid_cache", true],
["dom.mutation_events.enabled", true],
],
});