Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | <!DOCTYPE html> |
michael@0 | 2 | <html> |
michael@0 | 3 | <head> |
michael@0 | 4 | <script> |
michael@0 | 5 | |
michael@0 | 6 | // Morph a slim wrapper into an XPCWN by generating a cross-compartment wrapper |
michael@0 | 7 | // for it (we Morph in WrapperFactory::PrepareForWrapping). |
michael@0 | 8 | function makeNonSlim(elem) { |
michael@0 | 9 | window.parent.someCCW = elem; |
michael@0 | 10 | delete window.parent.someCCW; |
michael@0 | 11 | } |
michael@0 | 12 | |
michael@0 | 13 | function doTest() { |
michael@0 | 14 | |
michael@0 | 15 | // Get a slim wrapper for |form|. This lives in scope 1. |
michael@0 | 16 | var form = document.getElementById('myform'); |
michael@0 | 17 | |
michael@0 | 18 | // The gist here is that we want to create an input element that isn't part |
michael@0 | 19 | // of a form, so that its PreCreate hook will cause it to be parented to the |
michael@0 | 20 | // document. However, there are several considerations that make this more |
michael@0 | 21 | // complicted. |
michael@0 | 22 | // |
michael@0 | 23 | // First, the crashtest becomes non-deterministics if we morph |form| before |
michael@0 | 24 | // getting to scope 3 (as explained below). This means that we can't trigger |
michael@0 | 25 | // the PreCreate hook for |input|, because that will call WrapNativeParent |
michael@0 | 26 | // on the form, which will end up making a cross-compartment wrapper, which |
michael@0 | 27 | // will morph form. But this puts us in a pickle, because appendChild returns |
michael@0 | 28 | // the apppended child, which will trigger the PreCreate hook in |
michael@0 | 29 | // NativeInterface2JSObject. So we do this little hack where we append a buch |
michael@0 | 30 | // of dummy <div> children to the form, and use replaceChild (which returns |
michael@0 | 31 | // the replacer, not the replacee) to stick the input elements as children of |
michael@0 | 32 | // the form. |
michael@0 | 33 | // |
michael@0 | 34 | // Second, the crashtest can also be non-deterministics if the final call to |
michael@0 | 35 | // MoveWrappers iterates over the hashtable in such a way that it fixes up |
michael@0 | 36 | // the Document before it fixes up the Input. If so, the Input will become |
michael@0 | 37 | // orphaned, and we'll detect it and fix things up at the top of MoveWrapper. |
michael@0 | 38 | // Since we can't control the hashtable ordering here, we just make 100 input |
michael@0 | 39 | // elements, to make it a near-certainty (statistically) that we'll encounter |
michael@0 | 40 | // one of them during iteration before encountering the Document. |
michael@0 | 41 | // |
michael@0 | 42 | // With all this, this testcase deterministically crashes on my machine. Whew! |
michael@0 | 43 | |
michael@0 | 44 | // Create an input element. This isn't part of a form right now, so it gets |
michael@0 | 45 | // parented to the document. |
michael@0 | 46 | var inputs = []; |
michael@0 | 47 | var placeHolders = []; |
michael@0 | 48 | for (var i = 0; i < 100; ++i) { |
michael@0 | 49 | var dummyDiv = form.appendChild(document.createElement('div')); |
michael@0 | 50 | var input = document.createElement('input'); |
michael@0 | 51 | makeNonSlim(input); |
michael@0 | 52 | inputs.push(input); |
michael@0 | 53 | placeHolders.push(dummyDiv); |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | // Blow away the document, forcing a transplan of all the XPCWNs in scope. This |
michael@0 | 57 | // will transplant |input|, but |form| stays in the old scope (since it's slim). |
michael@0 | 58 | document.open(); |
michael@0 | 59 | document.close(); |
michael@0 | 60 | |
michael@0 | 61 | // Now we're in scope 2. Associate |input| with |form| so that the next call to |
michael@0 | 62 | // PreCreate will parent |input| to |form| rather than the document. But make |
michael@0 | 63 | // sure to do it with replaceChild, rather than appendChild, so that we don't |
michael@0 | 64 | // end up triggering the PreCreate hook for |form| in scope 2, which would make |
michael@0 | 65 | // make it non-slim. If we didn't, the ensuing call to MoveWrappers might find |
michael@0 | 66 | // |form| before |input| while iterating over the hashtable. If so, |form| |
michael@0 | 67 | // would be rescued as an orphan and everything would be fixed before getting to // |input|. |
michael@0 | 68 | for (var i = 0; i < inputs.length; ++i) |
michael@0 | 69 | form.replaceChild(inputs[i], placeHolders[i]); |
michael@0 | 70 | |
michael@0 | 71 | // Blow away the document a second time. This should cause the crash in |
michael@0 | 72 | // unpatched builds. |
michael@0 | 73 | document.open(); |
michael@0 | 74 | document.close(); |
michael@0 | 75 | } |
michael@0 | 76 | </script> |
michael@0 | 77 | </head> |
michael@0 | 78 | <body> |
michael@0 | 79 | <form id="myform"></form> |
michael@0 | 80 | </body> |
michael@0 | 81 | </html> |