1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/test/unit/head_channels.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,235 @@ 1.4 +/** 1.5 + * Read count bytes from stream and return as a String object 1.6 + */ 1.7 +function read_stream(stream, count) { 1.8 + /* assume stream has non-ASCII data */ 1.9 + var wrapper = 1.10 + Components.classes["@mozilla.org/binaryinputstream;1"] 1.11 + .createInstance(Components.interfaces.nsIBinaryInputStream); 1.12 + wrapper.setInputStream(stream); 1.13 + /* JS methods can be called with a maximum of 65535 arguments, and input 1.14 + streams don't have to return all the data they make .available() when 1.15 + asked to .read() that number of bytes. */ 1.16 + var data = []; 1.17 + while (count > 0) { 1.18 + var bytes = wrapper.readByteArray(Math.min(65535, count)); 1.19 + data.push(String.fromCharCode.apply(null, bytes)); 1.20 + count -= bytes.length; 1.21 + if (bytes.length == 0) 1.22 + do_throw("Nothing read from input stream!"); 1.23 + } 1.24 + return data.join(''); 1.25 +} 1.26 + 1.27 +const CL_EXPECT_FAILURE = 0x1; 1.28 +const CL_EXPECT_GZIP = 0x2; 1.29 +const CL_EXPECT_3S_DELAY = 0x4; 1.30 +const CL_SUSPEND = 0x8; 1.31 +const CL_ALLOW_UNKNOWN_CL = 0x10; 1.32 +const CL_EXPECT_LATE_FAILURE = 0x20; 1.33 +const CL_FROM_CACHE = 0x40; // Response must be from the cache 1.34 +const CL_NOT_FROM_CACHE = 0x80; // Response must NOT be from the cache 1.35 + 1.36 +const SUSPEND_DELAY = 3000; 1.37 + 1.38 +/** 1.39 + * A stream listener that calls a callback function with a specified 1.40 + * context and the received data when the channel is loaded. 1.41 + * 1.42 + * Signature of the closure: 1.43 + * void closure(in nsIRequest request, in ACString data, in JSObject context); 1.44 + * 1.45 + * This listener makes sure that various parts of the channel API are 1.46 + * implemented correctly and that the channel's status is a success code 1.47 + * (you can pass CL_EXPECT_FAILURE or CL_EXPECT_LATE_FAILURE as flags 1.48 + * to allow a failure code) 1.49 + * 1.50 + * Note that it also requires a valid content length on the channel and 1.51 + * is thus not fully generic. 1.52 + */ 1.53 +function ChannelListener(closure, ctx, flags) { 1.54 + this._closure = closure; 1.55 + this._closurectx = ctx; 1.56 + this._flags = flags; 1.57 +} 1.58 +ChannelListener.prototype = { 1.59 + _closure: null, 1.60 + _closurectx: null, 1.61 + _buffer: "", 1.62 + _got_onstartrequest: false, 1.63 + _got_onstoprequest: false, 1.64 + _contentLen: -1, 1.65 + _lastEvent: 0, 1.66 + 1.67 + QueryInterface: function(iid) { 1.68 + if (iid.equals(Components.interfaces.nsIStreamListener) || 1.69 + iid.equals(Components.interfaces.nsIRequestObserver) || 1.70 + iid.equals(Components.interfaces.nsISupports)) 1.71 + return this; 1.72 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.73 + }, 1.74 + 1.75 + onStartRequest: function(request, context) { 1.76 + try { 1.77 + if (this._got_onstartrequest) 1.78 + do_throw("Got second onStartRequest event!"); 1.79 + this._got_onstartrequest = true; 1.80 + this._lastEvent = Date.now(); 1.81 + 1.82 + request.QueryInterface(Components.interfaces.nsIChannel); 1.83 + try { 1.84 + this._contentLen = request.contentLength; 1.85 + } 1.86 + catch (ex) { 1.87 + if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) 1.88 + do_throw("Could not get contentLength"); 1.89 + } 1.90 + if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) 1.91 + do_throw("Content length is unknown in onStartRequest!"); 1.92 + 1.93 + if ((this._flags & CL_FROM_CACHE)) { 1.94 + request.QueryInterface(Ci.nsICachingChannel); 1.95 + if (!request.isFromCache()) { 1.96 + do_throw("Response is not from the cache (CL_FROM_CACHE)"); 1.97 + } 1.98 + } 1.99 + if ((this._flags & CL_NOT_FROM_CACHE)) { 1.100 + request.QueryInterface(Ci.nsICachingChannel); 1.101 + if (request.isFromCache()) { 1.102 + do_throw("Response is from the cache (CL_NOT_FROM_CACHE)"); 1.103 + } 1.104 + } 1.105 + 1.106 + if (this._flags & CL_SUSPEND) { 1.107 + request.suspend(); 1.108 + do_timeout(SUSPEND_DELAY, function() { request.resume(); }); 1.109 + } 1.110 + 1.111 + } catch (ex) { 1.112 + do_throw("Error in onStartRequest: " + ex); 1.113 + } 1.114 + }, 1.115 + 1.116 + onDataAvailable: function(request, context, stream, offset, count) { 1.117 + try { 1.118 + let current = Date.now(); 1.119 + 1.120 + if (!this._got_onstartrequest) 1.121 + do_throw("onDataAvailable without onStartRequest event!"); 1.122 + if (this._got_onstoprequest) 1.123 + do_throw("onDataAvailable after onStopRequest event!"); 1.124 + if (!request.isPending()) 1.125 + do_throw("request reports itself as not pending from onDataAvailable!"); 1.126 + if (this._flags & CL_EXPECT_FAILURE) 1.127 + do_throw("Got data despite expecting a failure"); 1.128 + 1.129 + if (current - this._lastEvent >= SUSPEND_DELAY && 1.130 + !(this._flags & CL_EXPECT_3S_DELAY)) 1.131 + do_throw("Data received after significant unexpected delay"); 1.132 + else if (current - this._lastEvent < SUSPEND_DELAY && 1.133 + this._flags & CL_EXPECT_3S_DELAY) 1.134 + do_throw("Data received sooner than expected"); 1.135 + else if (current - this._lastEvent >= SUSPEND_DELAY && 1.136 + this._flags & CL_EXPECT_3S_DELAY) 1.137 + this._flags &= ~CL_EXPECT_3S_DELAY; // No more delays expected 1.138 + 1.139 + this._buffer = this._buffer.concat(read_stream(stream, count)); 1.140 + this._lastEvent = current; 1.141 + } catch (ex) { 1.142 + do_throw("Error in onDataAvailable: " + ex); 1.143 + } 1.144 + }, 1.145 + 1.146 + onStopRequest: function(request, context, status) { 1.147 + try { 1.148 + var success = Components.isSuccessCode(status); 1.149 + if (!this._got_onstartrequest) 1.150 + do_throw("onStopRequest without onStartRequest event!"); 1.151 + if (this._got_onstoprequest) 1.152 + do_throw("Got second onStopRequest event!"); 1.153 + this._got_onstoprequest = true; 1.154 + if ((this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && success) 1.155 + do_throw("Should have failed to load URL (status is " + status.toString(16) + ")"); 1.156 + else if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && !success) 1.157 + do_throw("Failed to load URL: " + status.toString(16)); 1.158 + if (status != request.status) 1.159 + do_throw("request.status does not match status arg to onStopRequest!"); 1.160 + if (request.isPending()) 1.161 + do_throw("request reports itself as pending from onStopRequest!"); 1.162 + if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && 1.163 + !(this._flags & CL_EXPECT_GZIP) && 1.164 + this._contentLen != -1) 1.165 + do_check_eq(this._buffer.length, this._contentLen) 1.166 + } catch (ex) { 1.167 + do_throw("Error in onStopRequest: " + ex); 1.168 + } 1.169 + try { 1.170 + this._closure(request, this._buffer, this._closurectx); 1.171 + } catch (ex) { 1.172 + do_throw("Error in closure function: " + ex); 1.173 + } 1.174 + } 1.175 +}; 1.176 + 1.177 +var ES_ABORT_REDIRECT = 0x01; 1.178 + 1.179 +function ChannelEventSink(flags) 1.180 +{ 1.181 + this._flags = flags; 1.182 +} 1.183 + 1.184 +ChannelEventSink.prototype = { 1.185 + QueryInterface: function(iid) { 1.186 + if (iid.equals(Ci.nsIInterfaceRequestor) || 1.187 + iid.equals(Ci.nsISupports)) 1.188 + return this; 1.189 + throw Cr.NS_ERROR_NO_INTERFACE; 1.190 + }, 1.191 + 1.192 + getInterface: function(iid) { 1.193 + if (iid.equals(Ci.nsIChannelEventSink)) 1.194 + return this; 1.195 + throw Cr.NS_ERROR_NO_INTERFACE; 1.196 + }, 1.197 + 1.198 + asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) { 1.199 + if (this._flags & ES_ABORT_REDIRECT) 1.200 + throw Cr.NS_BINDING_ABORTED; 1.201 + 1.202 + callback.onRedirectVerifyCallback(Cr.NS_OK); 1.203 + } 1.204 +}; 1.205 + 1.206 + 1.207 +/** 1.208 + * Class that implements nsILoadContext. Use it as callbacks for channel when 1.209 + * test needs it. 1.210 + */ 1.211 +function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) { 1.212 + this.appId = appId; 1.213 + this.isInBrowserElement = inBrowserElement; 1.214 + this.usePrivateBrowsing = isPrivate; 1.215 + this.isContent = isContent; 1.216 +} 1.217 + 1.218 +LoadContextCallback.prototype = { 1.219 + associatedWindow: null, 1.220 + topWindow : null, 1.221 + isAppOfType: function(appType) { 1.222 + throw Cr.NS_ERROR_NOT_IMPLEMENTED; 1.223 + }, 1.224 + QueryInterface: function(iid) { 1.225 + if (iid.equals(Ci.nsILoadContext) || 1.226 + iid.equals(Ci.nsIInterfaceRequestor) || 1.227 + iid.equals(Ci.nsISupports)) { 1.228 + return this; 1.229 + } 1.230 + throw Cr.NS_ERROR_NO_INTERFACE; 1.231 + }, 1.232 + getInterface: function(iid) { 1.233 + if (iid.equals(Ci.nsILoadContext)) 1.234 + return this; 1.235 + throw Cr.NS_ERROR_NO_INTERFACE; 1.236 + }, 1.237 +} 1.238 +