Bug 786142 - Crashtest. r=me
authorBobby Holley <bobbyholley@gmail.com>
Mon, 11 Mar 2013 14:45:18 -0700
changeset 130848 ff3cf0d570a87733dfe98e10a27f4d822fd8b895
parent 130847 723ecd18f89865f88519d7375fafd85c882dc2a3
child 130849 c5a9a8bb2752197645c87c693ce5f8db0f1ac7bf
push idunknown
push userunknown
push dateunknown
reviewersme
bugs786142
milestone22.0a1
Bug 786142 - Crashtest. r=me
js/xpconnect/crashtests/786142-iframe.html
js/xpconnect/crashtests/786142.html
js/xpconnect/crashtests/crashtests.list
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/crashtests/786142-iframe.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+// Morph a slim wrapper into an XPCWN by generating a cross-compartment wrapper
+// for it (we Morph in WrapperFactory::PrepareForWrapping).
+function makeNonSlim(elem) {
+  window.parent.someCCW = elem;
+  delete window.parent.someCCW;
+}
+
+function doTest() {
+
+  // Get a slim wrapper for |form|. This lives in scope 1.
+  var form = document.getElementById('myform');
+
+  // The gist here is that we want to create an input element that isn't part
+  // of a form, so that its PreCreate hook will cause it to be parented to the
+  // document. However, there are several considerations that make this more
+  // complicted.
+  //
+  // First, the crashtest becomes non-deterministics if we morph |form| before
+  // getting to scope 3 (as explained below). This means that we can't trigger
+  // the PreCreate hook for |input|, because that will call WrapNativeParent
+  // on the form, which will end up making a cross-compartment wrapper, which
+  // will morph form. But this puts us in a pickle, because appendChild returns
+  // the apppended child, which will trigger the PreCreate hook in
+  // NativeInterface2JSObject. So we do this little hack where we append a buch
+  // of dummy <div> children to the form, and use replaceChild (which returns
+  // the replacer, not the replacee) to stick the input elements as children of
+  // the form.
+  //
+  // Second, the crashtest can also be non-deterministics if the final call to
+  // MoveWrappers iterates over the hashtable in such a way that it fixes up
+  // the Document before it fixes up the Input. If so, the Input will become
+  // orphaned, and we'll detect it and fix things up at the top of MoveWrapper.
+  // Since we can't control the hashtable ordering here, we just make 100 input
+  // elements, to make it a near-certainty (statistically) that we'll encounter
+  // one of them during iteration before encountering the Document.
+  //
+  // With all this, this testcase deterministically crashes on my machine. Whew!
+
+  // Create an input element. This isn't part of a form right now, so it gets
+  // parented to the document.
+  var inputs = [];
+  var placeHolders = [];
+  for (var i = 0; i < 100; ++i) {
+    var dummyDiv = form.appendChild(document.createElement('div'));
+    var input = document.createElement('input');
+    makeNonSlim(input);
+    inputs.push(input);
+    placeHolders.push(dummyDiv);
+  }
+
+  // Blow away the document, forcing a transplan of all the XPCWNs in scope. This
+  // will transplant |input|, but |form| stays in the old scope (since it's slim).
+  document.open();
+  document.close();
+
+  // Now we're in scope 2. Associate |input| with |form| so that the next call to
+  // PreCreate will parent |input| to |form| rather than the document. But make
+  // sure to do it with replaceChild, rather than appendChild, so that we don't
+  // end up triggering the PreCreate hook for |form| in scope 2, which would make
+  // make it non-slim. If we didn't, the ensuing call to MoveWrappers might find
+  // |form| before |input| while iterating over the hashtable. If so, |form|
+  // would be rescued as an orphan and everything would be fixed before getting to  // |input|.
+  for (var i = 0; i < inputs.length; ++i)
+    form.replaceChild(inputs[i], placeHolders[i]);
+
+  // Blow away the document a second time. This should cause the crash in
+  // unpatched builds.
+  document.open();
+  document.close();
+}
+</script>
+</head>
+<body>
+<form id="myform"></form>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/crashtests/786142.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+function go() {
+  // document.open() doesn't play well with reftest-wait, so we use an iframe.
+  var ifr = document.getElementById('ifr');
+  ifr.contentWindow.doTest();
+  document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body>
+  <iframe id="ifr" onload="go()" src="786142-iframe.html"></iframe>
+</body>
+</html>
--- a/js/xpconnect/crashtests/crashtests.list
+++ b/js/xpconnect/crashtests/crashtests.list
@@ -40,11 +40,12 @@ load 723465.html
 load 732870.html
 load 751995.html
 load 761831.html
 asserts(0-1) load 752038.html # We may hit bug 645229 here.
 asserts(1) load 753162.html # We hit bug 675518 or bug 680086 here.
 load 754311.html
 load 776328.html
 load 776333.html
+asserts(0-1) load 786142.html # We may hit bug 645229 here.
 load 791845.html
 load 797583.html
 load 833856.html