|
1 /* |
|
2 * This file is a modified version of netwerk/test/unit/head_channels.js |
|
3 * The changes consist of making it work in mochitest files and removing |
|
4 * unused code. |
|
5 */ |
|
6 |
|
7 /** |
|
8 * Read count bytes from stream and return as a String object |
|
9 */ |
|
10 function read_stream(stream, count) { |
|
11 /* assume stream has non-ASCII data */ |
|
12 var wrapper = |
|
13 Components.classes["@mozilla.org/binaryinputstream;1"] |
|
14 .createInstance(Components.interfaces.nsIBinaryInputStream); |
|
15 wrapper.setInputStream(stream); |
|
16 /* JS methods can be called with a maximum of 65535 arguments, and input |
|
17 streams don't have to return all the data they make .available() when |
|
18 asked to .read() that number of bytes. */ |
|
19 var data = []; |
|
20 while (count > 0) { |
|
21 var bytes = wrapper.readByteArray(Math.min(65535, count)); |
|
22 data.push(String.fromCharCode.apply(null, bytes)); |
|
23 count -= bytes.length; |
|
24 if (bytes.length == 0) |
|
25 throw("Nothing read from input stream!"); |
|
26 } |
|
27 return data.join(''); |
|
28 } |
|
29 |
|
30 const CL_EXPECT_FAILURE = 0x1; |
|
31 const CL_EXPECT_GZIP = 0x2; |
|
32 const CL_EXPECT_3S_DELAY = 0x4; |
|
33 const CL_SUSPEND = 0x8; |
|
34 const CL_ALLOW_UNKNOWN_CL = 0x10; |
|
35 const CL_EXPECT_LATE_FAILURE = 0x20; |
|
36 |
|
37 const SUSPEND_DELAY = 3000; |
|
38 |
|
39 /** |
|
40 * A stream listener that calls a callback function with a specified |
|
41 * context and the received data when the channel is loaded. |
|
42 * |
|
43 * Signature of the closure: |
|
44 * void closure(in nsIRequest request, in ACString data, in JSObject context); |
|
45 * |
|
46 * This listener makes sure that various parts of the channel API are |
|
47 * implemented correctly and that the channel's status is a success code |
|
48 * (you can pass CL_EXPECT_FAILURE or CL_EXPECT_LATE_FAILURE as flags |
|
49 * to allow a failure code) |
|
50 * |
|
51 * Note that it also requires a valid content length on the channel and |
|
52 * is thus not fully generic. |
|
53 */ |
|
54 function ChannelListener(closure, ctx, flags) { |
|
55 this._closure = closure; |
|
56 this._closurectx = ctx; |
|
57 this._flags = flags; |
|
58 } |
|
59 ChannelListener.prototype = { |
|
60 _closure: null, |
|
61 _closurectx: null, |
|
62 _buffer: "", |
|
63 _got_onstartrequest: false, |
|
64 _got_onstoprequest: false, |
|
65 _contentLen: -1, |
|
66 _lastEvent: 0, |
|
67 |
|
68 QueryInterface: function(iid) { |
|
69 if (iid.equals(Components.interfaces.nsIStreamListener) || |
|
70 iid.equals(Components.interfaces.nsIRequestObserver) || |
|
71 iid.equals(Components.interfaces.nsISupports)) |
|
72 return this; |
|
73 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
74 }, |
|
75 |
|
76 onStartRequest: function(request, context) { |
|
77 try { |
|
78 if (this._got_onstartrequest) |
|
79 throw("Got second onStartRequest event!"); |
|
80 this._got_onstartrequest = true; |
|
81 this._lastEvent = Date.now(); |
|
82 |
|
83 request.QueryInterface(Components.interfaces.nsIChannel); |
|
84 try { |
|
85 this._contentLen = request.contentLength; |
|
86 } |
|
87 catch (ex) { |
|
88 if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) |
|
89 throw("Could not get contentLength"); |
|
90 } |
|
91 if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) |
|
92 throw("Content length is unknown in onStartRequest!"); |
|
93 |
|
94 if (this._flags & CL_SUSPEND) { |
|
95 request.suspend(); |
|
96 do_timeout(SUSPEND_DELAY, function() { request.resume(); }); |
|
97 } |
|
98 |
|
99 } catch (ex) { |
|
100 throw("Error in onStartRequest: " + ex); |
|
101 } |
|
102 }, |
|
103 |
|
104 onDataAvailable: function(request, context, stream, offset, count) { |
|
105 try { |
|
106 var current = Date.now(); |
|
107 |
|
108 if (!this._got_onstartrequest) |
|
109 throw("onDataAvailable without onStartRequest event!"); |
|
110 if (this._got_onstoprequest) |
|
111 throw("onDataAvailable after onStopRequest event!"); |
|
112 if (!request.isPending()) |
|
113 throw("request reports itself as not pending from onDataAvailable!"); |
|
114 if (this._flags & CL_EXPECT_FAILURE) |
|
115 throw("Got data despite expecting a failure"); |
|
116 |
|
117 if (current - this._lastEvent >= SUSPEND_DELAY && |
|
118 !(this._flags & CL_EXPECT_3S_DELAY)) |
|
119 throw("Data received after significant unexpected delay"); |
|
120 else if (current - this._lastEvent < SUSPEND_DELAY && |
|
121 this._flags & CL_EXPECT_3S_DELAY) |
|
122 throw("Data received sooner than expected"); |
|
123 else if (current - this._lastEvent >= SUSPEND_DELAY && |
|
124 this._flags & CL_EXPECT_3S_DELAY) |
|
125 this._flags &= ~CL_EXPECT_3S_DELAY; // No more delays expected |
|
126 |
|
127 this._buffer = this._buffer.concat(read_stream(stream, count)); |
|
128 this._lastEvent = current; |
|
129 } catch (ex) { |
|
130 throw("Error in onDataAvailable: " + ex); |
|
131 } |
|
132 }, |
|
133 |
|
134 onStopRequest: function(request, context, status) { |
|
135 try { |
|
136 var success = Components.isSuccessCode(status); |
|
137 if (!this._got_onstartrequest) |
|
138 throw("onStopRequest without onStartRequest event!"); |
|
139 if (this._got_onstoprequest) |
|
140 throw("Got second onStopRequest event!"); |
|
141 this._got_onstoprequest = true; |
|
142 if ((this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && success) |
|
143 throw("Should have failed to load URL (status is " + status.toString(16) + ")"); |
|
144 else if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && !success) |
|
145 throw("Failed to load URL: " + status.toString(16)); |
|
146 if (status != request.status) |
|
147 throw("request.status does not match status arg to onStopRequest!"); |
|
148 if (request.isPending()) |
|
149 throw("request reports itself as pending from onStopRequest!"); |
|
150 if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && |
|
151 !(this._flags & CL_EXPECT_GZIP) && |
|
152 this._contentLen != -1) |
|
153 is(this._buffer.length, this._contentLen); |
|
154 } catch (ex) { |
|
155 throw("Error in onStopRequest: " + ex); |
|
156 } |
|
157 try { |
|
158 this._closure(request, this._buffer, this._closurectx); |
|
159 } catch (ex) { |
|
160 throw("Error in closure function: " + ex); |
|
161 } |
|
162 } |
|
163 }; |
|
164 |
|
165 /** |
|
166 * Class that implements nsILoadContext. Use it as callbacks for channel when |
|
167 * test needs it. |
|
168 */ |
|
169 function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) { |
|
170 this.appId = appId; |
|
171 this.isInBrowserElement = inBrowserElement; |
|
172 this.usePrivateBrowsing = isPrivate; |
|
173 this.isContent = isContent; |
|
174 } |
|
175 |
|
176 LoadContextCallback.prototype = { |
|
177 associatedWindow: null, |
|
178 topWindow : null, |
|
179 isAppOfType: function(appType) { |
|
180 throw Components.results.NS_ERROR_NOT_IMPLEMENTED; |
|
181 }, |
|
182 QueryInterface: function(iid) { |
|
183 if (iid == Ci.nsILoadContext || |
|
184 Ci.nsIInterfaceRequestor || |
|
185 Ci.nsISupports) { |
|
186 return this; |
|
187 } |
|
188 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
189 }, |
|
190 getInterface: function(iid) { |
|
191 if (iid.equals(Ci.nsILoadContext)) |
|
192 return this; |
|
193 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
194 }, |
|
195 } |