diff -r 000000000000 -r 6474c204b198 toolkit/devtools/webconsole/network-helper.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/devtools/webconsole/network-helper.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,486 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2007, Parakey Inc. + * All rights reserved. + * + * Redistribution and use of this software in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * * Neither the name of Parakey Inc. nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission of Parakey Inc. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Creator: + * Joe Hewitt + * Contributors + * John J. Barton (IBM Almaden) + * Jan Odvarko (Mozilla Corp.) + * Max Stepanov (Aptana Inc.) + * Rob Campbell (Mozilla Corp.) + * Hans Hillen (Paciello Group, Mozilla) + * Curtis Bartley (Mozilla Corp.) + * Mike Collins (IBM Almaden) + * Kevin Decker + * Mike Ratcliffe (Comartis AG) + * Hernan Rodríguez Colmeiro + * Austin Andrews + * Christoph Dorn + * Steven Roussey (AppCenter Inc, Network54) + * Mihai Sucan (Mozilla Corp.) + */ + +"use strict"; + +const {components, Cc, Ci, Cu} = require("chrome"); +loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); + +/** + * Helper object for networking stuff. + * + * Most of the following functions have been taken from the Firebug source. They + * have been modified to match the Firefox coding rules. + */ +let NetworkHelper = { + /** + * Converts aText with a given aCharset to unicode. + * + * @param string aText + * Text to convert. + * @param string aCharset + * Charset to convert the text to. + * @returns string + * Converted text. + */ + convertToUnicode: function NH_convertToUnicode(aText, aCharset) + { + let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + try { + conv.charset = aCharset || "UTF-8"; + return conv.ConvertToUnicode(aText); + } + catch (ex) { + return aText; + } + }, + + /** + * Reads all available bytes from aStream and converts them to aCharset. + * + * @param nsIInputStream aStream + * @param string aCharset + * @returns string + * UTF-16 encoded string based on the content of aStream and aCharset. + */ + readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset) + { + let text = null; + try { + text = NetUtil.readInputStreamToString(aStream, aStream.available()) + return this.convertToUnicode(text, aCharset); + } + catch (err) { + return text; + } + }, + + /** + * Reads the posted text from aRequest. + * + * @param nsIHttpChannel aRequest + * @param string aCharset + * The content document charset, used when reading the POSTed data. + * @returns string or null + * Returns the posted string if it was possible to read from aRequest + * otherwise null. + */ + readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset) + { + if (aRequest instanceof Ci.nsIUploadChannel) { + let iStream = aRequest.uploadStream; + + let isSeekableStream = false; + if (iStream instanceof Ci.nsISeekableStream) { + isSeekableStream = true; + } + + let prevOffset; + if (isSeekableStream) { + prevOffset = iStream.tell(); + iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); + } + + // Read data from the stream. + let text = this.readAndConvertFromStream(iStream, aCharset); + + // Seek locks the file, so seek to the beginning only if necko hasn't + // read it yet, since necko doesn't seek to 0 before reading (at lest + // not till 459384 is fixed). + if (isSeekableStream && prevOffset == 0) { + iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); + } + return text; + } + return null; + }, + + /** + * Reads the posted text from the page's cache. + * + * @param nsIDocShell aDocShell + * @param string aCharset + * @returns string or null + * Returns the posted string if it was possible to read from + * aDocShell otherwise null. + */ + readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset) + { + let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation); + return this.readPostTextFromPageViaWebNav(webNav, aCharset); + }, + + /** + * Reads the posted text from the page's cache, given an nsIWebNavigation + * object. + * + * @param nsIWebNavigation aWebNav + * @param string aCharset + * @returns string or null + * Returns the posted string if it was possible to read from + * aWebNav, otherwise null. + */ + readPostTextFromPageViaWebNav: + function NH_readPostTextFromPageViaWebNav(aWebNav, aCharset) + { + if (aWebNav instanceof Ci.nsIWebPageDescriptor) { + let descriptor = aWebNav.currentDescriptor; + + if (descriptor instanceof Ci.nsISHEntry && descriptor.postData && + descriptor instanceof Ci.nsISeekableStream) { + descriptor.seek(NS_SEEK_SET, 0); + + return this.readAndConvertFromStream(descriptor, aCharset); + } + } + return null; + }, + + /** + * Gets the web appId that is associated with aRequest. + * + * @param nsIHttpChannel aRequest + * @returns number|null + * The appId for the given request, if available. + */ + getAppIdForRequest: function NH_getAppIdForRequest(aRequest) + { + try { + return this.getRequestLoadContext(aRequest).appId; + } catch (ex) { + // request loadContent is not always available. + } + return null; + }, + + /** + * Gets the topFrameElement that is associated with aRequest. + * + * @param nsIHttpChannel aRequest + * @returns nsIDOMElement|null + * The top frame element for the given request, if available. + */ + getTopFrameForRequest: function NH_getTopFrameForRequest(aRequest) + { + try { + return this.getRequestLoadContext(aRequest).topFrameElement; + } catch (ex) { + // request loadContent is not always available. + } + return null; + }, + + /** + * Gets the nsIDOMWindow that is associated with aRequest. + * + * @param nsIHttpChannel aRequest + * @returns nsIDOMWindow or null + */ + getWindowForRequest: function NH_getWindowForRequest(aRequest) + { + try { + return this.getRequestLoadContext(aRequest).associatedWindow; + } catch (ex) { + // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no + // associatedWindow property. + } + return null; + }, + + /** + * Gets the nsILoadContext that is associated with aRequest. + * + * @param nsIHttpChannel aRequest + * @returns nsILoadContext or null + */ + getRequestLoadContext: function NH_getRequestLoadContext(aRequest) + { + try { + return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext); + } catch (ex) { } + + try { + return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext); + } catch (ex) { } + + return null; + }, + + /** + * Loads the content of aUrl from the cache. + * + * @param string aUrl + * URL to load the cached content for. + * @param string aCharset + * Assumed charset of the cached content. Used if there is no charset + * on the channel directly. + * @param function aCallback + * Callback that is called with the loaded cached content if available + * or null if something failed while getting the cached content. + */ + loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback) + { + let channel = NetUtil.newChannel(aUrl); + + // Ensure that we only read from the cache and not the server. + channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE | + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY; + + NetUtil.asyncFetch(channel, (aInputStream, aStatusCode, aRequest) => { + if (!components.isSuccessCode(aStatusCode)) { + aCallback(null); + return; + } + + // Try to get the encoding from the channel. If there is none, then use + // the passed assumed aCharset. + let aChannel = aRequest.QueryInterface(Ci.nsIChannel); + let contentCharset = aChannel.contentCharset || aCharset; + + // Read the content of the stream using contentCharset as encoding. + aCallback(this.readAndConvertFromStream(aInputStream, contentCharset)); + }); + }, + + /** + * Parse a raw Cookie header value. + * + * @param string aHeader + * The raw Cookie header value. + * @return array + * Array holding an object for each cookie. Each object holds the + * following properties: name and value. + */ + parseCookieHeader: function NH_parseCookieHeader(aHeader) + { + let cookies = aHeader.split(";"); + let result = []; + + cookies.forEach(function(aCookie) { + let equal = aCookie.indexOf("="); + let name = aCookie.substr(0, equal); + let value = aCookie.substr(equal + 1); + result.push({name: unescape(name.trim()), + value: unescape(value.trim())}); + }); + + return result; + }, + + /** + * Parse a raw Set-Cookie header value. + * + * @param string aHeader + * The raw Set-Cookie header value. + * @return array + * Array holding an object for each cookie. Each object holds the + * following properties: name, value, secure (boolean), httpOnly + * (boolean), path, domain and expires (ISO date string). + */ + parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader) + { + let rawCookies = aHeader.split(/\r\n|\n|\r/); + let cookies = []; + + rawCookies.forEach(function(aCookie) { + let equal = aCookie.indexOf("="); + let name = unescape(aCookie.substr(0, equal).trim()); + let parts = aCookie.substr(equal + 1).split(";"); + let value = unescape(parts.shift().trim()); + + let cookie = {name: name, value: value}; + + parts.forEach(function(aPart) { + let part = aPart.trim(); + if (part.toLowerCase() == "secure") { + cookie.secure = true; + } + else if (part.toLowerCase() == "httponly") { + cookie.httpOnly = true; + } + else if (part.indexOf("=") > -1) { + let pair = part.split("="); + pair[0] = pair[0].toLowerCase(); + if (pair[0] == "path" || pair[0] == "domain") { + cookie[pair[0]] = pair[1]; + } + else if (pair[0] == "expires") { + try { + pair[1] = pair[1].replace(/-/g, ' '); + cookie.expires = new Date(pair[1]).toISOString(); + } + catch (ex) { } + } + } + }); + + cookies.push(cookie); + }); + + return cookies; + }, + + // This is a list of all the mime category maps jviereck could find in the + // firebug code base. + mimeCategoryMap: { + "text/plain": "txt", + "text/html": "html", + "text/xml": "xml", + "text/xsl": "txt", + "text/xul": "txt", + "text/css": "css", + "text/sgml": "txt", + "text/rtf": "txt", + "text/x-setext": "txt", + "text/richtext": "txt", + "text/javascript": "js", + "text/jscript": "txt", + "text/tab-separated-values": "txt", + "text/rdf": "txt", + "text/xif": "txt", + "text/ecmascript": "js", + "text/vnd.curl": "txt", + "text/x-json": "json", + "text/x-js": "txt", + "text/js": "txt", + "text/vbscript": "txt", + "view-source": "txt", + "view-fragment": "txt", + "application/xml": "xml", + "application/xhtml+xml": "xml", + "application/atom+xml": "xml", + "application/rss+xml": "xml", + "application/vnd.mozilla.maybe.feed": "xml", + "application/vnd.mozilla.xul+xml": "xml", + "application/javascript": "js", + "application/x-javascript": "js", + "application/x-httpd-php": "txt", + "application/rdf+xml": "xml", + "application/ecmascript": "js", + "application/http-index-format": "txt", + "application/json": "json", + "application/x-js": "txt", + "multipart/mixed": "txt", + "multipart/x-mixed-replace": "txt", + "image/svg+xml": "svg", + "application/octet-stream": "bin", + "image/jpeg": "image", + "image/jpg": "image", + "image/gif": "image", + "image/png": "image", + "image/bmp": "image", + "application/x-shockwave-flash": "flash", + "video/x-flv": "flash", + "audio/mpeg3": "media", + "audio/x-mpeg-3": "media", + "video/mpeg": "media", + "video/x-mpeg": "media", + "audio/ogg": "media", + "application/ogg": "media", + "application/x-ogg": "media", + "application/x-midi": "media", + "audio/midi": "media", + "audio/x-mid": "media", + "audio/x-midi": "media", + "music/crescendo": "media", + "audio/wav": "media", + "audio/x-wav": "media", + "text/json": "json", + "application/x-json": "json", + "application/json-rpc": "json", + "application/x-web-app-manifest+json": "json", + }, + + /** + * Check if the given MIME type is a text-only MIME type. + * + * @param string aMimeType + * @return boolean + */ + isTextMimeType: function NH_isTextMimeType(aMimeType) + { + if (aMimeType.indexOf("text/") == 0) { + return true; + } + + // XML and JSON often come with custom MIME types, so in addition to the + // standard "application/xml" and "application/json", we also look for + // variants like "application/x-bigcorp+xml". For JSON we allow "+json" and + // "-json" as suffixes. + if (/^application\/\w+(?:[\.-]\w+)*(?:\+xml|[-+]json)$/.test(aMimeType)) { + return true; + } + + let category = this.mimeCategoryMap[aMimeType] || null; + switch (category) { + case "txt": + case "js": + case "json": + case "css": + case "html": + case "svg": + case "xml": + return true; + + default: + return false; + } + }, +}; + +for (let prop of Object.getOwnPropertyNames(NetworkHelper)) { + exports[prop] = NetworkHelper[prop]; +}