1d880b2fd9b529cb902f6d8c241401b5ec02ce35
[WebKit-https.git] / LayoutTests / fast / xmlhttprequest / xmlhttprequest-recursive-sync-event.html
1 <html>
2 <script>
3 function log(s)
4 {
5     var logDiv = document.getElementById("log");
6     logDiv.appendChild(document.createTextNode(s));
7     logDiv.appendChild(document.createElement("br"));
8 }
9
10 // This test is flaky because it depends on the size of the available native
11 // stack, the size of native stack frames, and the layering of function calls
12 // in the JavaScript VM code. Together, these variables determine when a
13 // StackOverflowError will be thrown in the course of this test of unbounded
14 // recursion.
15 //
16 // From experience with this test, StackOverflow error tends to be thrown in
17 // 2 scenarios:
18 //
19 // Scenario 1: Overflowed while calling xhr.open()
20 // ===============================================
21 // In this scenario, the VM is executing xhr.open() and succeeds in setting
22 // up the new request. In response to having opened the request, it tries to
23 // dispatch an event with readyState 1 (OPENED). Unfortunately, there is not
24 // enough stack and we get an overflow error and 1 console error message here.
25 //
26 // The error is thrown but is then eaten up by the EventDispatcher. Hence,
27 // onreadystatechange() back in JS land does not even see this error.
28 //
29 // open() eventually returns to onreadystatechange(), and we call xhr.send()
30 // next. xhr.send() overflows the stack again, and we get a 2nd error and
31 // a 2nd console error message.
32 //
33 // Note: the lastReadyStateObserved in this case is 4 because the event
34 // dispatch for OPENED failed to execute onreadystatechange().
35 //
36 // The total number of error messages seen is 2.
37 //
38 // Scenario 2: Overflowed while calling xhr.send()
39 // ===============================================
40 // In this scenario, the VM is executing xhr.send() and a readyState change
41 // to 4 (DONE) occurs. An event is dispatched and the VM tries to call
42 // onreadystatechange() but overflows the stack. Here, we see 1 error and
43 // 1 console error message.
44 //
45 // Since we never succeeded in reentering onreadystatechange(), we did not
46 // not set up anymore requests, and will not attempt to re-enter
47 // onreadystatechange() after this. Hence, we will not see anymore overflow
48 // errors nor console error messages.
49 //
50 // In contrast with scenario 1, we did not see an overflow error for xhr.open()
51 // before we saw the error for xhr.send().
52 //
53 // Note: lastReadyStateObserved in this case is 1 because the event
54 // dispatch for OPENED succeeded in executing onreadystatechange(), and
55 // lastReadyStateObserved was set to 1.
56 //
57 // The total number of error messages seen is 1.
58 //
59 // Flakiness
60 // =========
61 // Since we can end up with scenario 1 or 2 depending on variables outside the
62 // control of this test, the result of the test would be inherently flaky i.e.
63 // we can see 1 or 2 console error messages.
64 //
65 // That is unless we do some compensation. In the case of scenario 2, we can
66 // check if the lastReadyStateObserved is 1 which means we would have only seen
67 // one console error message. If so, we can call xhr.send() again just to
68 // trigger another one so that we'll have a total of 2.
69 //
70 // Note that in the compensation case, we'll need to call xhr.open() again
71 // before calling xhr.send(). This is because the previous xhr request has
72 // already been closed. Here, xhr.open() should not trigger an overflow error
73 // just like it didn't before the previous xhr.send() triggered the error (the
74 // fact that it succeeded without an error before means it will succeed this
75 // time too). And this leaves it up to our compensating call to xhr.send()
76 // to generate the 2nd expected overflow error and console error message.
77 //
78 // Note also that we'll need to make sure that we do this compensation only
79 // once, and not repeatedly do the compensating xhr.send() call on every
80 // onreadystatechange() frame on the return path.
81
82 lastReadyStateObserved = 0;
83 hasCompensatedAlready = false;
84
85 function test()
86 {
87     if (window.testRunner) {
88         testRunner.dumpAsText();
89     }
90     var xhr = new XMLHttpRequest();
91     xhr.onreadystatechange = function() {
92         lastReadyStateObserved = xhr.readyState;
93         if (xhr.readyState == 4) {
94             xhr.open("GET", "recurse.html", false);
95             xhr.send(null);
96
97             // Compensate for test flakiness if needed:
98             if (!hasCompensatedAlready && lastReadyStateObserved == 1) {
99                 xhr.open("GET", "recurse.html", false);
100                 xhr.send(null);
101                 hasCompensatedAlready = true;
102             }
103         }
104     };
105     xhr.open("GET", "recurse.html", false);
106     xhr.send(null);
107     log("PASS");
108 }
109 </script>
110 <body onload="test()">
111 This tests that having infinite recursion in XMLHttpRequest event handler does not crash.
112 <br>
113 <div id="log"></div>
114 </body>
115 </html>