1 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
2 * vim: sw=4 ts=4 sts=4 et filetype=javascript
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 // //////////////////////////////////////////////////////////////////////////////
14 const PR_UINT32_MAX = 0xffffffff;
16 const BinaryInputStream = Components.Constructor(
17 "@mozilla.org/binaryinputstream;1",
18 "nsIBinaryInputStream",
22 // //////////////////////////////////////////////////////////////////////////////
25 export var NetUtil = {
27 * Function to perform simple async copying from aSource (an input stream)
28 * to aSink (an output stream). The copy will happen on some background
29 * thread. Both streams will be closed when the copy completes.
32 * The input stream to read from
34 * The output stream to write to
35 * @param aCallback [optional]
36 * A function that will be called at copy completion with a single
37 * argument: the nsresult status code for the copy operation.
39 * @return An nsIRequest representing the copy operation (for example, this
40 * can be used to cancel the copying). The consumer can ignore the
41 * return value if desired.
43 asyncCopy: function NetUtil_asyncCopy(aSource, aSink, aCallback = null) {
44 if (!aSource || !aSink) {
45 let exception = new Components.Exception(
46 "Must have a source and a sink",
47 Cr.NS_ERROR_INVALID_ARG,
48 Components.stack.caller
53 // make a stream copier
55 "@mozilla.org/network/async-stream-copier;1"
56 ].createInstance(Ci.nsIAsyncStreamCopier2);
60 null /* Default event target */,
61 0 /* Default length */,
69 onStartRequest(aRequest) {},
70 onStopRequest(aRequest, aStatusCode) {
71 aCallback(aStatusCode);
79 copier.QueryInterface(Ci.nsIAsyncStreamCopier).asyncCopy(observer, null);
84 * Asynchronously opens a source and fetches the response. While the fetch
85 * is asynchronous, I/O may happen on the main thread. When reading from
86 * a local file, prefer using IOUtils methods instead.
89 * This argument can be one of the following:
90 * - An options object that will be passed to NetUtil.newChannel.
91 * - An existing nsIChannel.
92 * - An existing nsIInputStream.
93 * Using an nsIURI, nsIFile, or string spec directly is deprecated.
95 * The callback function that will be notified upon completion. It
96 * will get these arguments:
97 * 1) An nsIInputStream containing the data from aSource, if any.
98 * 2) The status code from opening the source.
99 * 3) Reference to the nsIRequest.
101 asyncFetch: function NetUtil_asyncFetch(aSource, aCallback) {
102 if (!aSource || !aCallback) {
103 let exception = new Components.Exception(
104 "Must have a source and a callback",
105 Cr.NS_ERROR_INVALID_ARG,
106 Components.stack.caller
111 // Create a pipe that will create our output stream that we can use once
112 // we have gotten all the data.
113 let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
114 pipe.init(true, true, 0, PR_UINT32_MAX, null);
116 // Create a listener that will give data to the pipe's output stream.
118 "@mozilla.org/network/simple-stream-listener;1"
119 ].createInstance(Ci.nsISimpleStreamListener);
120 listener.init(pipe.outputStream, {
121 onStartRequest(aRequest) {},
122 onStopRequest(aRequest, aStatusCode) {
123 pipe.outputStream.close();
124 aCallback(pipe.inputStream, aStatusCode, aRequest);
128 // Input streams are handled slightly differently from everything else.
129 if (aSource instanceof Ci.nsIInputStream) {
130 let pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(
131 Ci.nsIInputStreamPump
133 pump.init(aSource, 0, 0, true);
134 pump.asyncRead(listener, null);
138 let channel = aSource;
139 if (!(channel instanceof Ci.nsIChannel)) {
140 channel = this.newChannel(aSource);
144 channel.asyncOpen(listener);
146 let exception = new Components.Exception(
147 "Failed to open input source '" + channel.originalURI.spec + "'",
149 Components.stack.caller,
158 * Constructs a new URI for the given spec, character set, and base URI, or
162 * The string spec for the desired URI or an nsIFile.
163 * @param aOriginCharset [optional]
164 * The character set for the URI. Only used if aTarget is not an
166 * @param aBaseURI [optional]
167 * The base URI for the spec. Only used if aTarget is not an
170 * @return an nsIURI object.
172 newURI: function NetUtil_newURI(aTarget, aOriginCharset, aBaseURI) {
174 let exception = new Components.Exception(
175 "Must have a non-null string spec or nsIFile object",
176 Cr.NS_ERROR_INVALID_ARG,
177 Components.stack.caller
182 if (aTarget instanceof Ci.nsIFile) {
183 return Services.io.newFileURI(aTarget);
186 return Services.io.newURI(aTarget, aOriginCharset, aBaseURI);
190 * Constructs a new channel for the given source.
192 * Keep in mind that URIs coming from a webpage should *never* use the
193 * systemPrincipal as the loadingPrincipal.
196 * This argument used to be a string spec for the desired URI, an
197 * nsIURI, or an nsIFile. Now it should be an options object with
198 * the following properties:
201 * The full URI spec string, nsIURI or nsIFile to create the
203 * Note that this cannot be an nsIFile if you have to specify a
204 * non-default charset or base URI. Call NetUtil.newURI first if
205 * you need to construct an URI using those options.
208 * triggeringPrincipal:
211 * These will be used as values for the nsILoadInfo object on the
212 * created channel. For details, see nsILoadInfo in nsILoadInfo.idl
213 * loadUsingSystemPrincipal:
214 * Set this to true to use the system principal as
215 * loadingPrincipal. This must be omitted if loadingPrincipal or
216 * loadingNode are present.
217 * This should be used with care as it skips security checks.
219 * @return an nsIChannel object.
221 newChannel: function NetUtil_newChannel(aWhatToLoad) {
222 // Make sure the API is called using only the options object.
223 if (typeof aWhatToLoad != "object" || arguments.length != 1) {
224 throw new Components.Exception(
225 "newChannel requires a single object argument",
226 Cr.NS_ERROR_INVALID_ARG,
227 Components.stack.caller
235 loadUsingSystemPrincipal,
242 throw new Components.Exception(
243 "newChannel requires the 'uri' property on the options object.",
244 Cr.NS_ERROR_INVALID_ARG,
245 Components.stack.caller
249 if (typeof uri == "string" || uri instanceof Ci.nsIFile) {
250 uri = this.newURI(uri);
253 if (!loadingNode && !loadingPrincipal && !loadUsingSystemPrincipal) {
254 throw new Components.Exception(
255 "newChannel requires at least one of the 'loadingNode'," +
256 " 'loadingPrincipal', or 'loadUsingSystemPrincipal'" +
257 " properties on the options object.",
258 Cr.NS_ERROR_INVALID_ARG,
259 Components.stack.caller
263 if (loadUsingSystemPrincipal === true) {
264 if (loadingNode || loadingPrincipal) {
265 throw new Components.Exception(
266 "newChannel does not accept 'loadUsingSystemPrincipal'" +
267 " if the 'loadingNode' or 'loadingPrincipal' properties" +
268 " are present on the options object.",
269 Cr.NS_ERROR_INVALID_ARG,
270 Components.stack.caller
273 loadingPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
274 } else if (loadUsingSystemPrincipal !== undefined) {
275 throw new Components.Exception(
276 "newChannel requires the 'loadUsingSystemPrincipal'" +
277 " property on the options object to be 'true' or 'undefined'.",
278 Cr.NS_ERROR_INVALID_ARG,
279 Components.stack.caller
283 if (securityFlags === undefined) {
284 if (!loadUsingSystemPrincipal) {
285 throw new Components.Exception(
286 "newChannel requires the 'securityFlags' property on" +
287 " the options object unless loading from system principal.",
288 Cr.NS_ERROR_INVALID_ARG,
289 Components.stack.caller
292 securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
295 if (contentPolicyType === undefined) {
296 if (!loadUsingSystemPrincipal) {
297 throw new Components.Exception(
298 "newChannel requires the 'contentPolicyType' property on" +
299 " the options object unless loading from system principal.",
300 Cr.NS_ERROR_INVALID_ARG,
301 Components.stack.caller
304 contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
307 let channel = Services.io.newChannelFromURI(
310 loadingPrincipal || null,
311 triggeringPrincipal || null,
315 if (loadUsingSystemPrincipal) {
316 channel.loadInfo.allowDeprecatedSystemRequests = true;
321 newWebTransport: function NetUtil_newWebTransport() {
322 return Services.io.newWebTransport();
326 * Reads aCount bytes from aInputStream into a string.
328 * @param aInputStream
329 * The input stream to read from.
331 * The number of bytes to read from the stream.
332 * @param aOptions [optional]
334 * The character encoding of stream data.
336 * The character to replace unknown byte sequences.
337 * If unset, it causes an exceptions to be thrown.
339 * @return the bytes from the input stream in string form.
341 * @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream.
342 * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would
343 * block the calling thread (non-blocking mode only).
344 * @throws NS_ERROR_FAILURE if there are not enough bytes available to read
345 * aCount amount of data.
346 * @throws NS_ERROR_ILLEGAL_INPUT if aInputStream has invalid sequences
348 readInputStreamToString: function NetUtil_readInputStreamToString(
353 if (!(aInputStream instanceof Ci.nsIInputStream)) {
354 let exception = new Components.Exception(
355 "First argument should be an nsIInputStream",
356 Cr.NS_ERROR_INVALID_ARG,
357 Components.stack.caller
363 let exception = new Components.Exception(
364 "Non-zero amount of bytes must be specified",
365 Cr.NS_ERROR_INVALID_ARG,
366 Components.stack.caller
371 if (aOptions && "charset" in aOptions) {
372 let cis = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(
373 Ci.nsIConverterInputStream
376 // When replacement is set, the character that is unknown sequence
377 // replaces with aOptions.replacement character.
378 if (!("replacement" in aOptions)) {
379 // aOptions.replacement isn't set.
380 // If input stream has unknown sequences for aOptions.charset,
381 // throw NS_ERROR_ILLEGAL_INPUT.
382 aOptions.replacement = 0;
385 cis.init(aInputStream, aOptions.charset, aCount, aOptions.replacement);
387 cis.readString(-1, str);
391 // Adjust the stack so it throws at the caller's location.
392 throw new Components.Exception(
395 Components.stack.caller,
401 let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
402 Ci.nsIScriptableInputStream
404 sis.init(aInputStream);
406 return sis.readBytes(aCount);
408 // Adjust the stack so it throws at the caller's location.
409 throw new Components.Exception(
412 Components.stack.caller,
419 * Reads aCount bytes from aInputStream into a string.
421 * @param {nsIInputStream} aInputStream
422 * The input stream to read from.
423 * @param {integer} [aCount = aInputStream.available()]
424 * The number of bytes to read from the stream.
426 * @return the bytes from the input stream in string form.
428 * @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream.
429 * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would
430 * block the calling thread (non-blocking mode only).
431 * @throws NS_ERROR_FAILURE if there are not enough bytes available to read
432 * aCount amount of data.
434 readInputStream(aInputStream, aCount) {
435 if (!(aInputStream instanceof Ci.nsIInputStream)) {
436 let exception = new Components.Exception(
437 "First argument should be an nsIInputStream",
438 Cr.NS_ERROR_INVALID_ARG,
439 Components.stack.caller
445 aCount = aInputStream.available();
448 let stream = new BinaryInputStream(aInputStream);
449 let result = new ArrayBuffer(aCount);
450 stream.readArrayBuffer(result.byteLength, result);