Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /**
2 * Read count bytes from stream and return as a String object
3 */
4 function read_stream(stream, count) {
5 /* assume stream has non-ASCII data */
6 var wrapper =
7 Components.classes["@mozilla.org/binaryinputstream;1"]
8 .createInstance(Components.interfaces.nsIBinaryInputStream);
9 wrapper.setInputStream(stream);
10 /* JS methods can be called with a maximum of 65535 arguments, and input
11 streams don't have to return all the data they make .available() when
12 asked to .read() that number of bytes. */
13 var data = [];
14 while (count > 0) {
15 var bytes = wrapper.readByteArray(Math.min(65535, count));
16 data.push(String.fromCharCode.apply(null, bytes));
17 count -= bytes.length;
18 if (bytes.length == 0)
19 do_throw("Nothing read from input stream!");
20 }
21 return data.join('');
22 }
24 const CL_EXPECT_FAILURE = 0x1;
25 const CL_EXPECT_GZIP = 0x2;
26 const CL_EXPECT_3S_DELAY = 0x4;
27 const CL_SUSPEND = 0x8;
28 const CL_ALLOW_UNKNOWN_CL = 0x10;
29 const CL_EXPECT_LATE_FAILURE = 0x20;
30 const CL_FROM_CACHE = 0x40; // Response must be from the cache
31 const CL_NOT_FROM_CACHE = 0x80; // Response must NOT be from the cache
33 const SUSPEND_DELAY = 3000;
35 /**
36 * A stream listener that calls a callback function with a specified
37 * context and the received data when the channel is loaded.
38 *
39 * Signature of the closure:
40 * void closure(in nsIRequest request, in ACString data, in JSObject context);
41 *
42 * This listener makes sure that various parts of the channel API are
43 * implemented correctly and that the channel's status is a success code
44 * (you can pass CL_EXPECT_FAILURE or CL_EXPECT_LATE_FAILURE as flags
45 * to allow a failure code)
46 *
47 * Note that it also requires a valid content length on the channel and
48 * is thus not fully generic.
49 */
50 function ChannelListener(closure, ctx, flags) {
51 this._closure = closure;
52 this._closurectx = ctx;
53 this._flags = flags;
54 }
55 ChannelListener.prototype = {
56 _closure: null,
57 _closurectx: null,
58 _buffer: "",
59 _got_onstartrequest: false,
60 _got_onstoprequest: false,
61 _contentLen: -1,
62 _lastEvent: 0,
64 QueryInterface: function(iid) {
65 if (iid.equals(Components.interfaces.nsIStreamListener) ||
66 iid.equals(Components.interfaces.nsIRequestObserver) ||
67 iid.equals(Components.interfaces.nsISupports))
68 return this;
69 throw Components.results.NS_ERROR_NO_INTERFACE;
70 },
72 onStartRequest: function(request, context) {
73 try {
74 if (this._got_onstartrequest)
75 do_throw("Got second onStartRequest event!");
76 this._got_onstartrequest = true;
77 this._lastEvent = Date.now();
79 request.QueryInterface(Components.interfaces.nsIChannel);
80 try {
81 this._contentLen = request.contentLength;
82 }
83 catch (ex) {
84 if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
85 do_throw("Could not get contentLength");
86 }
87 if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
88 do_throw("Content length is unknown in onStartRequest!");
90 if ((this._flags & CL_FROM_CACHE)) {
91 request.QueryInterface(Ci.nsICachingChannel);
92 if (!request.isFromCache()) {
93 do_throw("Response is not from the cache (CL_FROM_CACHE)");
94 }
95 }
96 if ((this._flags & CL_NOT_FROM_CACHE)) {
97 request.QueryInterface(Ci.nsICachingChannel);
98 if (request.isFromCache()) {
99 do_throw("Response is from the cache (CL_NOT_FROM_CACHE)");
100 }
101 }
103 if (this._flags & CL_SUSPEND) {
104 request.suspend();
105 do_timeout(SUSPEND_DELAY, function() { request.resume(); });
106 }
108 } catch (ex) {
109 do_throw("Error in onStartRequest: " + ex);
110 }
111 },
113 onDataAvailable: function(request, context, stream, offset, count) {
114 try {
115 let current = Date.now();
117 if (!this._got_onstartrequest)
118 do_throw("onDataAvailable without onStartRequest event!");
119 if (this._got_onstoprequest)
120 do_throw("onDataAvailable after onStopRequest event!");
121 if (!request.isPending())
122 do_throw("request reports itself as not pending from onDataAvailable!");
123 if (this._flags & CL_EXPECT_FAILURE)
124 do_throw("Got data despite expecting a failure");
126 if (current - this._lastEvent >= SUSPEND_DELAY &&
127 !(this._flags & CL_EXPECT_3S_DELAY))
128 do_throw("Data received after significant unexpected delay");
129 else if (current - this._lastEvent < SUSPEND_DELAY &&
130 this._flags & CL_EXPECT_3S_DELAY)
131 do_throw("Data received sooner than expected");
132 else if (current - this._lastEvent >= SUSPEND_DELAY &&
133 this._flags & CL_EXPECT_3S_DELAY)
134 this._flags &= ~CL_EXPECT_3S_DELAY; // No more delays expected
136 this._buffer = this._buffer.concat(read_stream(stream, count));
137 this._lastEvent = current;
138 } catch (ex) {
139 do_throw("Error in onDataAvailable: " + ex);
140 }
141 },
143 onStopRequest: function(request, context, status) {
144 try {
145 var success = Components.isSuccessCode(status);
146 if (!this._got_onstartrequest)
147 do_throw("onStopRequest without onStartRequest event!");
148 if (this._got_onstoprequest)
149 do_throw("Got second onStopRequest event!");
150 this._got_onstoprequest = true;
151 if ((this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && success)
152 do_throw("Should have failed to load URL (status is " + status.toString(16) + ")");
153 else if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && !success)
154 do_throw("Failed to load URL: " + status.toString(16));
155 if (status != request.status)
156 do_throw("request.status does not match status arg to onStopRequest!");
157 if (request.isPending())
158 do_throw("request reports itself as pending from onStopRequest!");
159 if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) &&
160 !(this._flags & CL_EXPECT_GZIP) &&
161 this._contentLen != -1)
162 do_check_eq(this._buffer.length, this._contentLen)
163 } catch (ex) {
164 do_throw("Error in onStopRequest: " + ex);
165 }
166 try {
167 this._closure(request, this._buffer, this._closurectx);
168 } catch (ex) {
169 do_throw("Error in closure function: " + ex);
170 }
171 }
172 };
174 var ES_ABORT_REDIRECT = 0x01;
176 function ChannelEventSink(flags)
177 {
178 this._flags = flags;
179 }
181 ChannelEventSink.prototype = {
182 QueryInterface: function(iid) {
183 if (iid.equals(Ci.nsIInterfaceRequestor) ||
184 iid.equals(Ci.nsISupports))
185 return this;
186 throw Cr.NS_ERROR_NO_INTERFACE;
187 },
189 getInterface: function(iid) {
190 if (iid.equals(Ci.nsIChannelEventSink))
191 return this;
192 throw Cr.NS_ERROR_NO_INTERFACE;
193 },
195 asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
196 if (this._flags & ES_ABORT_REDIRECT)
197 throw Cr.NS_BINDING_ABORTED;
199 callback.onRedirectVerifyCallback(Cr.NS_OK);
200 }
201 };
204 /**
205 * Class that implements nsILoadContext. Use it as callbacks for channel when
206 * test needs it.
207 */
208 function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) {
209 this.appId = appId;
210 this.isInBrowserElement = inBrowserElement;
211 this.usePrivateBrowsing = isPrivate;
212 this.isContent = isContent;
213 }
215 LoadContextCallback.prototype = {
216 associatedWindow: null,
217 topWindow : null,
218 isAppOfType: function(appType) {
219 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
220 },
221 QueryInterface: function(iid) {
222 if (iid.equals(Ci.nsILoadContext) ||
223 iid.equals(Ci.nsIInterfaceRequestor) ||
224 iid.equals(Ci.nsISupports)) {
225 return this;
226 }
227 throw Cr.NS_ERROR_NO_INTERFACE;
228 },
229 getInterface: function(iid) {
230 if (iid.equals(Ci.nsILoadContext))
231 return this;
232 throw Cr.NS_ERROR_NO_INTERFACE;
233 },
234 }