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 +}