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