netwerk/base/src/NetUtil.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/NetUtil.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,329 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: sw=4 ts=4 sts=4 et filetype=javascript
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +this.EXPORTED_SYMBOLS = [
    1.11 +  "NetUtil",
    1.12 +];
    1.13 +
    1.14 +/**
    1.15 + * Necko utilities
    1.16 + */
    1.17 +
    1.18 +////////////////////////////////////////////////////////////////////////////////
    1.19 +//// Constants
    1.20 +
    1.21 +const Ci = Components.interfaces;
    1.22 +const Cc = Components.classes;
    1.23 +const Cr = Components.results;
    1.24 +const Cu = Components.utils;
    1.25 +
    1.26 +const PR_UINT32_MAX = 0xffffffff;
    1.27 +
    1.28 +////////////////////////////////////////////////////////////////////////////////
    1.29 +//// NetUtil Object
    1.30 +
    1.31 +this.NetUtil = {
    1.32 +    /**
    1.33 +     * Function to perform simple async copying from aSource (an input stream)
    1.34 +     * to aSink (an output stream).  The copy will happen on some background
    1.35 +     * thread.  Both streams will be closed when the copy completes.
    1.36 +     *
    1.37 +     * @param aSource
    1.38 +     *        The input stream to read from
    1.39 +     * @param aSink
    1.40 +     *        The output stream to write to
    1.41 +     * @param aCallback [optional]
    1.42 +     *        A function that will be called at copy completion with a single
    1.43 +     *        argument: the nsresult status code for the copy operation.
    1.44 +     *
    1.45 +     * @return An nsIRequest representing the copy operation (for example, this
    1.46 +     *         can be used to cancel the copying).  The consumer can ignore the
    1.47 +     *         return value if desired.
    1.48 +     */
    1.49 +    asyncCopy: function NetUtil_asyncCopy(aSource, aSink,
    1.50 +                                          aCallback = null)
    1.51 +    {
    1.52 +        if (!aSource || !aSink) {
    1.53 +            let exception = new Components.Exception(
    1.54 +                "Must have a source and a sink",
    1.55 +                Cr.NS_ERROR_INVALID_ARG,
    1.56 +                Components.stack.caller
    1.57 +            );
    1.58 +            throw exception;
    1.59 +        }
    1.60 +
    1.61 +        // make a stream copier
    1.62 +        var copier = Cc["@mozilla.org/network/async-stream-copier;1"].
    1.63 +            createInstance(Ci.nsIAsyncStreamCopier2);
    1.64 +        copier.init(aSource, aSink,
    1.65 +                    null /* Default event target */,
    1.66 +                    0 /* Default length */,
    1.67 +                    true, true /* Auto-close */);
    1.68 +
    1.69 +        var observer;
    1.70 +        if (aCallback) {
    1.71 +            observer = {
    1.72 +                onStartRequest: function(aRequest, aContext) {},
    1.73 +                onStopRequest: function(aRequest, aContext, aStatusCode) {
    1.74 +                    aCallback(aStatusCode);
    1.75 +                }
    1.76 +            }
    1.77 +        } else {
    1.78 +            observer = null;
    1.79 +        }
    1.80 +
    1.81 +        // start the copying
    1.82 +        copier.QueryInterface(Ci.nsIAsyncStreamCopier).asyncCopy(observer, null);
    1.83 +        return copier;
    1.84 +    },
    1.85 +
    1.86 +    /**
    1.87 +     * Asynchronously opens a source and fetches the response.  A source can be
    1.88 +     * an nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream.  The
    1.89 +     * provided callback will get an input stream containing the response, the
    1.90 +     * result code, and a reference to the request.
    1.91 +     *
    1.92 +     * @param aSource
    1.93 +     *        The nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream
    1.94 +     *        to open.
    1.95 +     * @param aCallback
    1.96 +     *        The callback function that will be notified upon completion.  It
    1.97 +     *        will get two arguments:
    1.98 +     *        1) An nsIInputStream containing the data from aSource, if any.
    1.99 +     *        2) The status code from opening the source.
   1.100 +     *        3) Reference to the nsIRequest.
   1.101 +     */
   1.102 +    asyncFetch: function NetUtil_asyncOpen(aSource, aCallback)
   1.103 +    {
   1.104 +        if (!aSource || !aCallback) {
   1.105 +            let exception = new Components.Exception(
   1.106 +                "Must have a source and a callback",
   1.107 +                Cr.NS_ERROR_INVALID_ARG,
   1.108 +                Components.stack.caller
   1.109 +            );
   1.110 +            throw exception;
   1.111 +        }
   1.112 +
   1.113 +        // Create a pipe that will create our output stream that we can use once
   1.114 +        // we have gotten all the data.
   1.115 +        let pipe = Cc["@mozilla.org/pipe;1"].
   1.116 +                   createInstance(Ci.nsIPipe);
   1.117 +        pipe.init(true, true, 0, PR_UINT32_MAX, null);
   1.118 +
   1.119 +        // Create a listener that will give data to the pipe's output stream.
   1.120 +        let listener = Cc["@mozilla.org/network/simple-stream-listener;1"].
   1.121 +                       createInstance(Ci.nsISimpleStreamListener);
   1.122 +        listener.init(pipe.outputStream, {
   1.123 +            onStartRequest: function(aRequest, aContext) {},
   1.124 +            onStopRequest: function(aRequest, aContext, aStatusCode) {
   1.125 +                pipe.outputStream.close();
   1.126 +                aCallback(pipe.inputStream, aStatusCode, aRequest);
   1.127 +            }
   1.128 +        });
   1.129 +
   1.130 +        // Input streams are handled slightly differently from everything else.
   1.131 +        if (aSource instanceof Ci.nsIInputStream) {
   1.132 +            let pump = Cc["@mozilla.org/network/input-stream-pump;1"].
   1.133 +                       createInstance(Ci.nsIInputStreamPump);
   1.134 +            pump.init(aSource, -1, -1, 0, 0, true);
   1.135 +            pump.asyncRead(listener, null);
   1.136 +            return;
   1.137 +        }
   1.138 +
   1.139 +        let channel = aSource;
   1.140 +        if (!(channel instanceof Ci.nsIChannel)) {
   1.141 +            channel = this.newChannel(aSource);
   1.142 +        }
   1.143 +
   1.144 +        try {
   1.145 +            channel.asyncOpen(listener, null);
   1.146 +        }
   1.147 +        catch (e) {
   1.148 +            let exception = new Components.Exception(
   1.149 +                "Failed to open input source '" + channel.originalURI.spec + "'",
   1.150 +                e.result,
   1.151 +                Components.stack.caller,
   1.152 +                aSource,
   1.153 +                e
   1.154 +            );
   1.155 +            throw exception;
   1.156 +        }
   1.157 +    },
   1.158 +
   1.159 +    /**
   1.160 +     * Constructs a new URI for the given spec, character set, and base URI, or
   1.161 +     * an nsIFile.
   1.162 +     *
   1.163 +     * @param aTarget
   1.164 +     *        The string spec for the desired URI or an nsIFile.
   1.165 +     * @param aOriginCharset [optional]
   1.166 +     *        The character set for the URI.  Only used if aTarget is not an
   1.167 +     *        nsIFile.
   1.168 +     * @param aBaseURI [optional]
   1.169 +     *        The base URI for the spec.  Only used if aTarget is not an
   1.170 +     *        nsIFile.
   1.171 +     *
   1.172 +     * @return an nsIURI object.
   1.173 +     */
   1.174 +    newURI: function NetUtil_newURI(aTarget, aOriginCharset, aBaseURI)
   1.175 +    {
   1.176 +        if (!aTarget) {
   1.177 +            let exception = new Components.Exception(
   1.178 +                "Must have a non-null string spec or nsIFile object",
   1.179 +                Cr.NS_ERROR_INVALID_ARG,
   1.180 +                Components.stack.caller
   1.181 +            );
   1.182 +            throw exception;
   1.183 +        }
   1.184 +
   1.185 +        if (aTarget instanceof Ci.nsIFile) {
   1.186 +            return this.ioService.newFileURI(aTarget);
   1.187 +        }
   1.188 +
   1.189 +        return this.ioService.newURI(aTarget, aOriginCharset, aBaseURI);
   1.190 +    },
   1.191 +
   1.192 +    /**
   1.193 +     * Constructs a new channel for the given spec, character set, and base URI,
   1.194 +     * or nsIURI, or nsIFile.
   1.195 +     *
   1.196 +     * @param aWhatToLoad
   1.197 +     *        The string spec for the desired URI, an nsIURI, or an nsIFile.
   1.198 +     * @param aOriginCharset [optional]
   1.199 +     *        The character set for the URI.  Only used if aWhatToLoad is a
   1.200 +     *        string.
   1.201 +     * @param aBaseURI [optional]
   1.202 +     *        The base URI for the spec.  Only used if aWhatToLoad is a string.
   1.203 +     *
   1.204 +     * @return an nsIChannel object.
   1.205 +     */
   1.206 +    newChannel: function NetUtil_newChannel(aWhatToLoad, aOriginCharset,
   1.207 +                                            aBaseURI)
   1.208 +    {
   1.209 +        if (!aWhatToLoad) {
   1.210 +            let exception = new Components.Exception(
   1.211 +                "Must have a non-null string spec, nsIURI, or nsIFile object",
   1.212 +                Cr.NS_ERROR_INVALID_ARG,
   1.213 +                Components.stack.caller
   1.214 +            );
   1.215 +            throw exception;
   1.216 +        }
   1.217 +
   1.218 +        let uri = aWhatToLoad;
   1.219 +        if (!(aWhatToLoad instanceof Ci.nsIURI)) {
   1.220 +            // We either have a string or an nsIFile that we'll need a URI for.
   1.221 +            uri = this.newURI(aWhatToLoad, aOriginCharset, aBaseURI);
   1.222 +        }
   1.223 +
   1.224 +        return this.ioService.newChannelFromURI(uri);
   1.225 +    },
   1.226 +
   1.227 +    /**
   1.228 +     * Reads aCount bytes from aInputStream into a string.
   1.229 +     *
   1.230 +     * @param aInputStream
   1.231 +     *        The input stream to read from.
   1.232 +     * @param aCount
   1.233 +     *        The number of bytes to read from the stream.
   1.234 +     * @param aOptions [optional]
   1.235 +     *        charset
   1.236 +     *          The character encoding of stream data.
   1.237 +     *        replacement
   1.238 +     *          The character to replace unknown byte sequences.
   1.239 +     *          If unset, it causes an exceptions to be thrown.
   1.240 +     *
   1.241 +     * @return the bytes from the input stream in string form.
   1.242 +     *
   1.243 +     * @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream.
   1.244 +     * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would
   1.245 +     *         block the calling thread (non-blocking mode only).
   1.246 +     * @throws NS_ERROR_FAILURE if there are not enough bytes available to read
   1.247 +     *         aCount amount of data.
   1.248 +     * @throws NS_ERROR_ILLEGAL_INPUT if aInputStream has invalid sequences
   1.249 +     */
   1.250 +    readInputStreamToString: function NetUtil_readInputStreamToString(aInputStream,
   1.251 +                                                                      aCount,
   1.252 +                                                                      aOptions)
   1.253 +    {
   1.254 +        if (!(aInputStream instanceof Ci.nsIInputStream)) {
   1.255 +            let exception = new Components.Exception(
   1.256 +                "First argument should be an nsIInputStream",
   1.257 +                Cr.NS_ERROR_INVALID_ARG,
   1.258 +                Components.stack.caller
   1.259 +            );
   1.260 +            throw exception;
   1.261 +        }
   1.262 +
   1.263 +        if (!aCount) {
   1.264 +            let exception = new Components.Exception(
   1.265 +                "Non-zero amount of bytes must be specified",
   1.266 +                Cr.NS_ERROR_INVALID_ARG,
   1.267 +                Components.stack.caller
   1.268 +            );
   1.269 +            throw exception;
   1.270 +        }
   1.271 +
   1.272 +        if (aOptions && "charset" in aOptions) {
   1.273 +          let cis = Cc["@mozilla.org/intl/converter-input-stream;1"].
   1.274 +                    createInstance(Ci.nsIConverterInputStream);
   1.275 +          try {
   1.276 +            // When replacement is set, the character that is unknown sequence 
   1.277 +            // replaces with aOptions.replacement character.
   1.278 +            if (!("replacement" in aOptions)) {
   1.279 +              // aOptions.replacement isn't set.
   1.280 +              // If input stream has unknown sequences for aOptions.charset,
   1.281 +              // throw NS_ERROR_ILLEGAL_INPUT.
   1.282 +              aOptions.replacement = 0;
   1.283 +            }
   1.284 +
   1.285 +            cis.init(aInputStream, aOptions.charset, aCount,
   1.286 +                     aOptions.replacement);
   1.287 +            let str = {};
   1.288 +            cis.readString(-1, str);
   1.289 +            cis.close();
   1.290 +            return str.value;
   1.291 +          }
   1.292 +          catch (e) {
   1.293 +            // Adjust the stack so it throws at the caller's location.
   1.294 +            throw new Components.Exception(e.message, e.result,
   1.295 +                                           Components.stack.caller, e.data);
   1.296 +          }
   1.297 +        }
   1.298 +
   1.299 +        let sis = Cc["@mozilla.org/scriptableinputstream;1"].
   1.300 +                  createInstance(Ci.nsIScriptableInputStream);
   1.301 +        sis.init(aInputStream);
   1.302 +        try {
   1.303 +            return sis.readBytes(aCount);
   1.304 +        }
   1.305 +        catch (e) {
   1.306 +            // Adjust the stack so it throws at the caller's location.
   1.307 +            throw new Components.Exception(e.message, e.result,
   1.308 +                                           Components.stack.caller, e.data);
   1.309 +        }
   1.310 +    },
   1.311 +
   1.312 +    /**
   1.313 +     * Returns a reference to nsIIOService.
   1.314 +     *
   1.315 +     * @return a reference to nsIIOService.
   1.316 +     */
   1.317 +    get ioService()
   1.318 +    {
   1.319 +        delete this.ioService;
   1.320 +        return this.ioService = Cc["@mozilla.org/network/io-service;1"].
   1.321 +                                getService(Ci.nsIIOService);
   1.322 +    },
   1.323 +};
   1.324 +
   1.325 +////////////////////////////////////////////////////////////////////////////////
   1.326 +//// Initialization
   1.327 +
   1.328 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
   1.329 +
   1.330 +// Define our lazy getters.
   1.331 +XPCOMUtils.defineLazyServiceGetter(this, "ioUtil", "@mozilla.org/io-util;1",
   1.332 +                                   "nsIIOUtil");

mercurial