toolkit/devtools/webconsole/network-helper.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/devtools/webconsole/network-helper.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,486 @@
     1.4 +/* vim:set ts=2 sw=2 sts=2 et: */
     1.5 +/*
     1.6 + * Software License Agreement (BSD License)
     1.7 + *
     1.8 + * Copyright (c) 2007, Parakey Inc.
     1.9 + * All rights reserved.
    1.10 + *
    1.11 + * Redistribution and use of this software in source and binary forms, with or without modification,
    1.12 + * are permitted provided that the following conditions are met:
    1.13 + *
    1.14 + * * Redistributions of source code must retain the above
    1.15 + *   copyright notice, this list of conditions and the
    1.16 + *   following disclaimer.
    1.17 + *
    1.18 + * * Redistributions in binary form must reproduce the above
    1.19 + *   copyright notice, this list of conditions and the
    1.20 + *   following disclaimer in the documentation and/or other
    1.21 + *   materials provided with the distribution.
    1.22 + *
    1.23 + * * Neither the name of Parakey Inc. nor the names of its
    1.24 + *   contributors may be used to endorse or promote products
    1.25 + *   derived from this software without specific prior
    1.26 + *   written permission of Parakey Inc.
    1.27 + *
    1.28 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    1.29 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    1.30 + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    1.31 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    1.32 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.33 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
    1.34 + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    1.35 + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.36 + */
    1.37 +
    1.38 +/*
    1.39 + * Creator:
    1.40 + *  Joe Hewitt
    1.41 + * Contributors
    1.42 + *  John J. Barton (IBM Almaden)
    1.43 + *  Jan Odvarko (Mozilla Corp.)
    1.44 + *  Max Stepanov (Aptana Inc.)
    1.45 + *  Rob Campbell (Mozilla Corp.)
    1.46 + *  Hans Hillen (Paciello Group, Mozilla)
    1.47 + *  Curtis Bartley (Mozilla Corp.)
    1.48 + *  Mike Collins (IBM Almaden)
    1.49 + *  Kevin Decker
    1.50 + *  Mike Ratcliffe (Comartis AG)
    1.51 + *  Hernan Rodríguez Colmeiro
    1.52 + *  Austin Andrews
    1.53 + *  Christoph Dorn
    1.54 + *  Steven Roussey (AppCenter Inc, Network54)
    1.55 + *  Mihai Sucan (Mozilla Corp.)
    1.56 + */
    1.57 +
    1.58 +"use strict";
    1.59 +
    1.60 +const {components, Cc, Ci, Cu} = require("chrome");
    1.61 +loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
    1.62 +
    1.63 +/**
    1.64 + * Helper object for networking stuff.
    1.65 + *
    1.66 + * Most of the following functions have been taken from the Firebug source. They
    1.67 + * have been modified to match the Firefox coding rules.
    1.68 + */
    1.69 +let NetworkHelper = {
    1.70 +  /**
    1.71 +   * Converts aText with a given aCharset to unicode.
    1.72 +   *
    1.73 +   * @param string aText
    1.74 +   *        Text to convert.
    1.75 +   * @param string aCharset
    1.76 +   *        Charset to convert the text to.
    1.77 +   * @returns string
    1.78 +   *          Converted text.
    1.79 +   */
    1.80 +  convertToUnicode: function NH_convertToUnicode(aText, aCharset)
    1.81 +  {
    1.82 +    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
    1.83 +               createInstance(Ci.nsIScriptableUnicodeConverter);
    1.84 +    try {
    1.85 +      conv.charset = aCharset || "UTF-8";
    1.86 +      return conv.ConvertToUnicode(aText);
    1.87 +    }
    1.88 +    catch (ex) {
    1.89 +      return aText;
    1.90 +    }
    1.91 +  },
    1.92 +
    1.93 +  /**
    1.94 +   * Reads all available bytes from aStream and converts them to aCharset.
    1.95 +   *
    1.96 +   * @param nsIInputStream aStream
    1.97 +   * @param string aCharset
    1.98 +   * @returns string
    1.99 +   *          UTF-16 encoded string based on the content of aStream and aCharset.
   1.100 +   */
   1.101 +  readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset)
   1.102 +  {
   1.103 +    let text = null;
   1.104 +    try {
   1.105 +      text = NetUtil.readInputStreamToString(aStream, aStream.available())
   1.106 +      return this.convertToUnicode(text, aCharset);
   1.107 +    }
   1.108 +    catch (err) {
   1.109 +      return text;
   1.110 +    }
   1.111 +  },
   1.112 +
   1.113 +   /**
   1.114 +   * Reads the posted text from aRequest.
   1.115 +   *
   1.116 +   * @param nsIHttpChannel aRequest
   1.117 +   * @param string aCharset
   1.118 +   *        The content document charset, used when reading the POSTed data.
   1.119 +   * @returns string or null
   1.120 +   *          Returns the posted string if it was possible to read from aRequest
   1.121 +   *          otherwise null.
   1.122 +   */
   1.123 +  readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset)
   1.124 +  {
   1.125 +    if (aRequest instanceof Ci.nsIUploadChannel) {
   1.126 +      let iStream = aRequest.uploadStream;
   1.127 +
   1.128 +      let isSeekableStream = false;
   1.129 +      if (iStream instanceof Ci.nsISeekableStream) {
   1.130 +        isSeekableStream = true;
   1.131 +      }
   1.132 +
   1.133 +      let prevOffset;
   1.134 +      if (isSeekableStream) {
   1.135 +        prevOffset = iStream.tell();
   1.136 +        iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
   1.137 +      }
   1.138 +
   1.139 +      // Read data from the stream.
   1.140 +      let text = this.readAndConvertFromStream(iStream, aCharset);
   1.141 +
   1.142 +      // Seek locks the file, so seek to the beginning only if necko hasn't
   1.143 +      // read it yet, since necko doesn't seek to 0 before reading (at lest
   1.144 +      // not till 459384 is fixed).
   1.145 +      if (isSeekableStream && prevOffset == 0) {
   1.146 +        iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
   1.147 +      }
   1.148 +      return text;
   1.149 +    }
   1.150 +    return null;
   1.151 +  },
   1.152 +
   1.153 +  /**
   1.154 +   * Reads the posted text from the page's cache.
   1.155 +   *
   1.156 +   * @param nsIDocShell aDocShell
   1.157 +   * @param string aCharset
   1.158 +   * @returns string or null
   1.159 +   *          Returns the posted string if it was possible to read from
   1.160 +   *          aDocShell otherwise null.
   1.161 +   */
   1.162 +  readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset)
   1.163 +  {
   1.164 +    let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
   1.165 +    return this.readPostTextFromPageViaWebNav(webNav, aCharset);
   1.166 +  },
   1.167 +
   1.168 +  /**
   1.169 +   * Reads the posted text from the page's cache, given an nsIWebNavigation
   1.170 +   * object.
   1.171 +   *
   1.172 +   * @param nsIWebNavigation aWebNav
   1.173 +   * @param string aCharset
   1.174 +   * @returns string or null
   1.175 +   *          Returns the posted string if it was possible to read from
   1.176 +   *          aWebNav, otherwise null.
   1.177 +   */
   1.178 +  readPostTextFromPageViaWebNav:
   1.179 +  function NH_readPostTextFromPageViaWebNav(aWebNav, aCharset)
   1.180 +  {
   1.181 +    if (aWebNav instanceof Ci.nsIWebPageDescriptor) {
   1.182 +      let descriptor = aWebNav.currentDescriptor;
   1.183 +
   1.184 +      if (descriptor instanceof Ci.nsISHEntry && descriptor.postData &&
   1.185 +          descriptor instanceof Ci.nsISeekableStream) {
   1.186 +        descriptor.seek(NS_SEEK_SET, 0);
   1.187 +
   1.188 +        return this.readAndConvertFromStream(descriptor, aCharset);
   1.189 +      }
   1.190 +    }
   1.191 +    return null;
   1.192 +  },
   1.193 +
   1.194 +  /**
   1.195 +   * Gets the web appId that is associated with aRequest.
   1.196 +   *
   1.197 +   * @param nsIHttpChannel aRequest
   1.198 +   * @returns number|null
   1.199 +   *          The appId for the given request, if available.
   1.200 +   */
   1.201 +  getAppIdForRequest: function NH_getAppIdForRequest(aRequest)
   1.202 +  {
   1.203 +    try {
   1.204 +      return this.getRequestLoadContext(aRequest).appId;
   1.205 +    } catch (ex) {
   1.206 +      // request loadContent is not always available.
   1.207 +    }
   1.208 +    return null;
   1.209 +  },
   1.210 +
   1.211 +  /**
   1.212 +   * Gets the topFrameElement that is associated with aRequest.
   1.213 +   *
   1.214 +   * @param nsIHttpChannel aRequest
   1.215 +   * @returns nsIDOMElement|null
   1.216 +   *          The top frame element for the given request, if available.
   1.217 +   */
   1.218 +  getTopFrameForRequest: function NH_getTopFrameForRequest(aRequest)
   1.219 +  {
   1.220 +    try {
   1.221 +      return this.getRequestLoadContext(aRequest).topFrameElement;
   1.222 +    } catch (ex) {
   1.223 +      // request loadContent is not always available.
   1.224 +    }
   1.225 +    return null;
   1.226 +  },
   1.227 +
   1.228 +  /**
   1.229 +   * Gets the nsIDOMWindow that is associated with aRequest.
   1.230 +   *
   1.231 +   * @param nsIHttpChannel aRequest
   1.232 +   * @returns nsIDOMWindow or null
   1.233 +   */
   1.234 +  getWindowForRequest: function NH_getWindowForRequest(aRequest)
   1.235 +  {
   1.236 +    try {
   1.237 +      return this.getRequestLoadContext(aRequest).associatedWindow;
   1.238 +    } catch (ex) {
   1.239 +      // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
   1.240 +      // associatedWindow property.
   1.241 +    }
   1.242 +    return null;
   1.243 +  },
   1.244 +
   1.245 +  /**
   1.246 +   * Gets the nsILoadContext that is associated with aRequest.
   1.247 +   *
   1.248 +   * @param nsIHttpChannel aRequest
   1.249 +   * @returns nsILoadContext or null
   1.250 +   */
   1.251 +  getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
   1.252 +  {
   1.253 +    try {
   1.254 +      return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
   1.255 +    } catch (ex) { }
   1.256 +
   1.257 +    try {
   1.258 +      return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
   1.259 +    } catch (ex) { }
   1.260 +
   1.261 +    return null;
   1.262 +  },
   1.263 +
   1.264 +  /**
   1.265 +   * Loads the content of aUrl from the cache.
   1.266 +   *
   1.267 +   * @param string aUrl
   1.268 +   *        URL to load the cached content for.
   1.269 +   * @param string aCharset
   1.270 +   *        Assumed charset of the cached content. Used if there is no charset
   1.271 +   *        on the channel directly.
   1.272 +   * @param function aCallback
   1.273 +   *        Callback that is called with the loaded cached content if available
   1.274 +   *        or null if something failed while getting the cached content.
   1.275 +   */
   1.276 +  loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback)
   1.277 +  {
   1.278 +    let channel = NetUtil.newChannel(aUrl);
   1.279 +
   1.280 +    // Ensure that we only read from the cache and not the server.
   1.281 +    channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE |
   1.282 +      Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
   1.283 +      Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
   1.284 +
   1.285 +    NetUtil.asyncFetch(channel, (aInputStream, aStatusCode, aRequest) => {
   1.286 +      if (!components.isSuccessCode(aStatusCode)) {
   1.287 +        aCallback(null);
   1.288 +        return;
   1.289 +      }
   1.290 +
   1.291 +      // Try to get the encoding from the channel. If there is none, then use
   1.292 +      // the passed assumed aCharset.
   1.293 +      let aChannel = aRequest.QueryInterface(Ci.nsIChannel);
   1.294 +      let contentCharset = aChannel.contentCharset || aCharset;
   1.295 +
   1.296 +      // Read the content of the stream using contentCharset as encoding.
   1.297 +      aCallback(this.readAndConvertFromStream(aInputStream, contentCharset));
   1.298 +    });
   1.299 +  },
   1.300 +
   1.301 +  /**
   1.302 +   * Parse a raw Cookie header value.
   1.303 +   *
   1.304 +   * @param string aHeader
   1.305 +   *        The raw Cookie header value.
   1.306 +   * @return array
   1.307 +   *         Array holding an object for each cookie. Each object holds the
   1.308 +   *         following properties: name and value.
   1.309 +   */
   1.310 +  parseCookieHeader: function NH_parseCookieHeader(aHeader)
   1.311 +  {
   1.312 +    let cookies = aHeader.split(";");
   1.313 +    let result = [];
   1.314 +
   1.315 +    cookies.forEach(function(aCookie) {
   1.316 +      let equal = aCookie.indexOf("=");
   1.317 +      let name = aCookie.substr(0, equal);
   1.318 +      let value = aCookie.substr(equal + 1);
   1.319 +      result.push({name: unescape(name.trim()),
   1.320 +                   value: unescape(value.trim())});
   1.321 +    });
   1.322 +
   1.323 +    return result;
   1.324 +  },
   1.325 +
   1.326 +  /**
   1.327 +   * Parse a raw Set-Cookie header value.
   1.328 +   *
   1.329 +   * @param string aHeader
   1.330 +   *        The raw Set-Cookie header value.
   1.331 +   * @return array
   1.332 +   *         Array holding an object for each cookie. Each object holds the
   1.333 +   *         following properties: name, value, secure (boolean), httpOnly
   1.334 +   *         (boolean), path, domain and expires (ISO date string).
   1.335 +   */
   1.336 +  parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader)
   1.337 +  {
   1.338 +    let rawCookies = aHeader.split(/\r\n|\n|\r/);
   1.339 +    let cookies = [];
   1.340 +
   1.341 +    rawCookies.forEach(function(aCookie) {
   1.342 +      let equal = aCookie.indexOf("=");
   1.343 +      let name = unescape(aCookie.substr(0, equal).trim());
   1.344 +      let parts = aCookie.substr(equal + 1).split(";");
   1.345 +      let value = unescape(parts.shift().trim());
   1.346 +
   1.347 +      let cookie = {name: name, value: value};
   1.348 +
   1.349 +      parts.forEach(function(aPart) {
   1.350 +        let part = aPart.trim();
   1.351 +        if (part.toLowerCase() == "secure") {
   1.352 +          cookie.secure = true;
   1.353 +        }
   1.354 +        else if (part.toLowerCase() == "httponly") {
   1.355 +          cookie.httpOnly = true;
   1.356 +        }
   1.357 +        else if (part.indexOf("=") > -1) {
   1.358 +          let pair = part.split("=");
   1.359 +          pair[0] = pair[0].toLowerCase();
   1.360 +          if (pair[0] == "path" || pair[0] == "domain") {
   1.361 +            cookie[pair[0]] = pair[1];
   1.362 +          }
   1.363 +          else if (pair[0] == "expires") {
   1.364 +            try {
   1.365 +              pair[1] = pair[1].replace(/-/g, ' ');
   1.366 +              cookie.expires = new Date(pair[1]).toISOString();
   1.367 +            }
   1.368 +            catch (ex) { }
   1.369 +          }
   1.370 +        }
   1.371 +      });
   1.372 +
   1.373 +      cookies.push(cookie);
   1.374 +    });
   1.375 +
   1.376 +    return cookies;
   1.377 +  },
   1.378 +
   1.379 +  // This is a list of all the mime category maps jviereck could find in the
   1.380 +  // firebug code base.
   1.381 +  mimeCategoryMap: {
   1.382 +    "text/plain": "txt",
   1.383 +    "text/html": "html",
   1.384 +    "text/xml": "xml",
   1.385 +    "text/xsl": "txt",
   1.386 +    "text/xul": "txt",
   1.387 +    "text/css": "css",
   1.388 +    "text/sgml": "txt",
   1.389 +    "text/rtf": "txt",
   1.390 +    "text/x-setext": "txt",
   1.391 +    "text/richtext": "txt",
   1.392 +    "text/javascript": "js",
   1.393 +    "text/jscript": "txt",
   1.394 +    "text/tab-separated-values": "txt",
   1.395 +    "text/rdf": "txt",
   1.396 +    "text/xif": "txt",
   1.397 +    "text/ecmascript": "js",
   1.398 +    "text/vnd.curl": "txt",
   1.399 +    "text/x-json": "json",
   1.400 +    "text/x-js": "txt",
   1.401 +    "text/js": "txt",
   1.402 +    "text/vbscript": "txt",
   1.403 +    "view-source": "txt",
   1.404 +    "view-fragment": "txt",
   1.405 +    "application/xml": "xml",
   1.406 +    "application/xhtml+xml": "xml",
   1.407 +    "application/atom+xml": "xml",
   1.408 +    "application/rss+xml": "xml",
   1.409 +    "application/vnd.mozilla.maybe.feed": "xml",
   1.410 +    "application/vnd.mozilla.xul+xml": "xml",
   1.411 +    "application/javascript": "js",
   1.412 +    "application/x-javascript": "js",
   1.413 +    "application/x-httpd-php": "txt",
   1.414 +    "application/rdf+xml": "xml",
   1.415 +    "application/ecmascript": "js",
   1.416 +    "application/http-index-format": "txt",
   1.417 +    "application/json": "json",
   1.418 +    "application/x-js": "txt",
   1.419 +    "multipart/mixed": "txt",
   1.420 +    "multipart/x-mixed-replace": "txt",
   1.421 +    "image/svg+xml": "svg",
   1.422 +    "application/octet-stream": "bin",
   1.423 +    "image/jpeg": "image",
   1.424 +    "image/jpg": "image",
   1.425 +    "image/gif": "image",
   1.426 +    "image/png": "image",
   1.427 +    "image/bmp": "image",
   1.428 +    "application/x-shockwave-flash": "flash",
   1.429 +    "video/x-flv": "flash",
   1.430 +    "audio/mpeg3": "media",
   1.431 +    "audio/x-mpeg-3": "media",
   1.432 +    "video/mpeg": "media",
   1.433 +    "video/x-mpeg": "media",
   1.434 +    "audio/ogg": "media",
   1.435 +    "application/ogg": "media",
   1.436 +    "application/x-ogg": "media",
   1.437 +    "application/x-midi": "media",
   1.438 +    "audio/midi": "media",
   1.439 +    "audio/x-mid": "media",
   1.440 +    "audio/x-midi": "media",
   1.441 +    "music/crescendo": "media",
   1.442 +    "audio/wav": "media",
   1.443 +    "audio/x-wav": "media",
   1.444 +    "text/json": "json",
   1.445 +    "application/x-json": "json",
   1.446 +    "application/json-rpc": "json",
   1.447 +    "application/x-web-app-manifest+json": "json",
   1.448 +  },
   1.449 +
   1.450 +  /**
   1.451 +   * Check if the given MIME type is a text-only MIME type.
   1.452 +   *
   1.453 +   * @param string aMimeType
   1.454 +   * @return boolean
   1.455 +   */
   1.456 +  isTextMimeType: function NH_isTextMimeType(aMimeType)
   1.457 +  {
   1.458 +    if (aMimeType.indexOf("text/") == 0) {
   1.459 +      return true;
   1.460 +    }
   1.461 +
   1.462 +    // XML and JSON often come with custom MIME types, so in addition to the
   1.463 +    // standard "application/xml" and "application/json", we also look for
   1.464 +    // variants like "application/x-bigcorp+xml". For JSON we allow "+json" and
   1.465 +    // "-json" as suffixes.
   1.466 +    if (/^application\/\w+(?:[\.-]\w+)*(?:\+xml|[-+]json)$/.test(aMimeType)) {
   1.467 +      return true;
   1.468 +    }
   1.469 +
   1.470 +    let category = this.mimeCategoryMap[aMimeType] || null;
   1.471 +    switch (category) {
   1.472 +      case "txt":
   1.473 +      case "js":
   1.474 +      case "json":
   1.475 +      case "css":
   1.476 +      case "html":
   1.477 +      case "svg":
   1.478 +      case "xml":
   1.479 +        return true;
   1.480 +
   1.481 +      default:
   1.482 +        return false;
   1.483 +    }
   1.484 +  },
   1.485 +};
   1.486 +
   1.487 +for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
   1.488 +  exports[prop] = NetworkHelper[prop];
   1.489 +}

mercurial