Backed out 35 changesets (bug 941158, bug 972518, bug 959520, bug 986063, bug 948895...
[gecko.git] / toolkit / devtools / webconsole / network-helper.js
blob0df3fb050109f68d5c4a5a5b179e040831be8f05
1 /* vim:set ts=2 sw=2 sts=2 et: */
2 /*
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2007, Parakey Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use of this software in source and binary forms, with or without modification,
9  * are permitted provided that the following conditions are met:
10  *
11  * * Redistributions of source code must retain the above
12  *   copyright notice, this list of conditions and the
13  *   following disclaimer.
14  *
15  * * Redistributions in binary form must reproduce the above
16  *   copyright notice, this list of conditions and the
17  *   following disclaimer in the documentation and/or other
18  *   materials provided with the distribution.
19  *
20  * * Neither the name of Parakey Inc. nor the names of its
21  *   contributors may be used to endorse or promote products
22  *   derived from this software without specific prior
23  *   written permission of Parakey Inc.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
27  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
31  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
36  * Creator:
37  *  Joe Hewitt
38  * Contributors
39  *  John J. Barton (IBM Almaden)
40  *  Jan Odvarko (Mozilla Corp.)
41  *  Max Stepanov (Aptana Inc.)
42  *  Rob Campbell (Mozilla Corp.)
43  *  Hans Hillen (Paciello Group, Mozilla)
44  *  Curtis Bartley (Mozilla Corp.)
45  *  Mike Collins (IBM Almaden)
46  *  Kevin Decker
47  *  Mike Ratcliffe (Comartis AG)
48  *  Hernan Rodríguez Colmeiro
49  *  Austin Andrews
50  *  Christoph Dorn
51  *  Steven Roussey (AppCenter Inc, Network54)
52  *  Mihai Sucan (Mozilla Corp.)
53  */
55 const {components, Cc, Ci, Cu} = require("chrome");
56 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
58 /**
59  * Helper object for networking stuff.
60  *
61  * Most of the following functions have been taken from the Firebug source. They
62  * have been modified to match the Firefox coding rules.
63  */
64 let NetworkHelper = {
65   /**
66    * Converts aText with a given aCharset to unicode.
67    *
68    * @param string aText
69    *        Text to convert.
70    * @param string aCharset
71    *        Charset to convert the text to.
72    * @returns string
73    *          Converted text.
74    */
75   convertToUnicode: function NH_convertToUnicode(aText, aCharset)
76   {
77     let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
78                createInstance(Ci.nsIScriptableUnicodeConverter);
79     try {
80       conv.charset = aCharset || "UTF-8";
81       return conv.ConvertToUnicode(aText);
82     }
83     catch (ex) {
84       return aText;
85     }
86   },
88   /**
89    * Reads all available bytes from aStream and converts them to aCharset.
90    *
91    * @param nsIInputStream aStream
92    * @param string aCharset
93    * @returns string
94    *          UTF-16 encoded string based on the content of aStream and aCharset.
95    */
96   readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset)
97   {
98     let text = null;
99     try {
100       text = NetUtil.readInputStreamToString(aStream, aStream.available())
101       return this.convertToUnicode(text, aCharset);
102     }
103     catch (err) {
104       return text;
105     }
106   },
108    /**
109    * Reads the posted text from aRequest.
110    *
111    * @param nsIHttpChannel aRequest
112    * @param string aCharset
113    *        The content document charset, used when reading the POSTed data.
114    * @returns string or null
115    *          Returns the posted string if it was possible to read from aRequest
116    *          otherwise null.
117    */
118   readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset)
119   {
120     if (aRequest instanceof Ci.nsIUploadChannel) {
121       let iStream = aRequest.uploadStream;
123       let isSeekableStream = false;
124       if (iStream instanceof Ci.nsISeekableStream) {
125         isSeekableStream = true;
126       }
128       let prevOffset;
129       if (isSeekableStream) {
130         prevOffset = iStream.tell();
131         iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
132       }
134       // Read data from the stream.
135       let text = this.readAndConvertFromStream(iStream, aCharset);
137       // Seek locks the file, so seek to the beginning only if necko hasn't
138       // read it yet, since necko doesn't seek to 0 before reading (at lest
139       // not till 459384 is fixed).
140       if (isSeekableStream && prevOffset == 0) {
141         iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
142       }
143       return text;
144     }
145     return null;
146   },
148   /**
149    * Reads the posted text from the page's cache.
150    *
151    * @param nsIDocShell aDocShell
152    * @param string aCharset
153    * @returns string or null
154    *          Returns the posted string if it was possible to read from
155    *          aDocShell otherwise null.
156    */
157   readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset)
158   {
159     let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
160     return this.readPostTextFromPageViaWebNav(webNav, aCharset);
161   },
163   /**
164    * Reads the posted text from the page's cache, given an nsIWebNavigation
165    * object.
166    *
167    * @param nsIWebNavigation aWebNav
168    * @param string aCharset
169    * @returns string or null
170    *          Returns the posted string if it was possible to read from
171    *          aWebNav, otherwise null.
172    */
173   readPostTextFromPageViaWebNav:
174   function NH_readPostTextFromPageViaWebNav(aWebNav, aCharset)
175   {
176     if (aWebNav instanceof Ci.nsIWebPageDescriptor) {
177       let descriptor = aWebNav.currentDescriptor;
179       if (descriptor instanceof Ci.nsISHEntry && descriptor.postData &&
180           descriptor instanceof Ci.nsISeekableStream) {
181         descriptor.seek(NS_SEEK_SET, 0);
183         return this.readAndConvertFromStream(descriptor, aCharset);
184       }
185     }
186     return null;
187   },
189   /**
190    * Gets the nsIDOMWindow that is associated with aRequest.
191    *
192    * @param nsIHttpChannel aRequest
193    * @returns nsIDOMWindow or null
194    */
195   getWindowForRequest: function NH_getWindowForRequest(aRequest)
196   {
197     try {
198       return this.getRequestLoadContext(aRequest).associatedWindow;
199     } catch (ex) {
200       // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
201       // associatedWindow property.
202     }
203     return null;
204   },
206   /**
207    * Gets the nsILoadContext that is associated with aRequest.
208    *
209    * @param nsIHttpChannel aRequest
210    * @returns nsILoadContext or null
211    */
212   getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
213   {
214     try {
215       return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
216     } catch (ex) { }
218     try {
219       return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
220     } catch (ex) { }
222     return null;
223   },
225   /**
226    * Loads the content of aUrl from the cache.
227    *
228    * @param string aUrl
229    *        URL to load the cached content for.
230    * @param string aCharset
231    *        Assumed charset of the cached content. Used if there is no charset
232    *        on the channel directly.
233    * @param function aCallback
234    *        Callback that is called with the loaded cached content if available
235    *        or null if something failed while getting the cached content.
236    */
237   loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback)
238   {
239     let channel = NetUtil.newChannel(aUrl);
241     // Ensure that we only read from the cache and not the server.
242     channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE |
243       Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
244       Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
246     NetUtil.asyncFetch(channel, function (aInputStream, aStatusCode, aRequest) {
247       if (!components.isSuccessCode(aStatusCode)) {
248         aCallback(null);
249         return;
250       }
252       // Try to get the encoding from the channel. If there is none, then use
253       // the passed assumed aCharset.
254       let aChannel = aRequest.QueryInterface(Ci.nsIChannel);
255       let contentCharset = aChannel.contentCharset || aCharset;
257       // Read the content of the stream using contentCharset as encoding.
258       aCallback(this.readAndConvertFromStream(aInputStream, contentCharset));
259     });
260   },
262   /**
263    * Parse a raw Cookie header value.
264    *
265    * @param string aHeader
266    *        The raw Cookie header value.
267    * @return array
268    *         Array holding an object for each cookie. Each object holds the
269    *         following properties: name and value.
270    */
271   parseCookieHeader: function NH_parseCookieHeader(aHeader)
272   {
273     let cookies = aHeader.split(";");
274     let result = [];
276     cookies.forEach(function(aCookie) {
277       let equal = aCookie.indexOf("=");
278       let name = aCookie.substr(0, equal);
279       let value = aCookie.substr(equal + 1);
280       result.push({name: unescape(name.trim()),
281                    value: unescape(value.trim())});
282     });
284     return result;
285   },
287   /**
288    * Parse a raw Set-Cookie header value.
289    *
290    * @param string aHeader
291    *        The raw Set-Cookie header value.
292    * @return array
293    *         Array holding an object for each cookie. Each object holds the
294    *         following properties: name, value, secure (boolean), httpOnly
295    *         (boolean), path, domain and expires (ISO date string).
296    */
297   parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader)
298   {
299     let rawCookies = aHeader.split(/\r\n|\n|\r/);
300     let cookies = [];
302     rawCookies.forEach(function(aCookie) {
303       let equal = aCookie.indexOf("=");
304       let name = unescape(aCookie.substr(0, equal).trim());
305       let parts = aCookie.substr(equal + 1).split(";");
306       let value = unescape(parts.shift().trim());
308       let cookie = {name: name, value: value};
310       parts.forEach(function(aPart) {
311         let part = aPart.trim();
312         if (part.toLowerCase() == "secure") {
313           cookie.secure = true;
314         }
315         else if (part.toLowerCase() == "httponly") {
316           cookie.httpOnly = true;
317         }
318         else if (part.indexOf("=") > -1) {
319           let pair = part.split("=");
320           pair[0] = pair[0].toLowerCase();
321           if (pair[0] == "path" || pair[0] == "domain") {
322             cookie[pair[0]] = pair[1];
323           }
324           else if (pair[0] == "expires") {
325             try {
326               pair[1] = pair[1].replace(/-/g, ' ');
327               cookie.expires = new Date(pair[1]).toISOString();
328             }
329             catch (ex) { }
330           }
331         }
332       });
334       cookies.push(cookie);
335     });
337     return cookies;
338   },
340   // This is a list of all the mime category maps jviereck could find in the
341   // firebug code base.
342   mimeCategoryMap: {
343     "text/plain": "txt",
344     "text/html": "html",
345     "text/xml": "xml",
346     "text/xsl": "txt",
347     "text/xul": "txt",
348     "text/css": "css",
349     "text/sgml": "txt",
350     "text/rtf": "txt",
351     "text/x-setext": "txt",
352     "text/richtext": "txt",
353     "text/javascript": "js",
354     "text/jscript": "txt",
355     "text/tab-separated-values": "txt",
356     "text/rdf": "txt",
357     "text/xif": "txt",
358     "text/ecmascript": "js",
359     "text/vnd.curl": "txt",
360     "text/x-json": "json",
361     "text/x-js": "txt",
362     "text/js": "txt",
363     "text/vbscript": "txt",
364     "view-source": "txt",
365     "view-fragment": "txt",
366     "application/xml": "xml",
367     "application/xhtml+xml": "xml",
368     "application/atom+xml": "xml",
369     "application/rss+xml": "xml",
370     "application/vnd.mozilla.maybe.feed": "xml",
371     "application/vnd.mozilla.xul+xml": "xml",
372     "application/javascript": "js",
373     "application/x-javascript": "js",
374     "application/x-httpd-php": "txt",
375     "application/rdf+xml": "xml",
376     "application/ecmascript": "js",
377     "application/http-index-format": "txt",
378     "application/json": "json",
379     "application/x-js": "txt",
380     "multipart/mixed": "txt",
381     "multipart/x-mixed-replace": "txt",
382     "image/svg+xml": "svg",
383     "application/octet-stream": "bin",
384     "image/jpeg": "image",
385     "image/jpg": "image",
386     "image/gif": "image",
387     "image/png": "image",
388     "image/bmp": "image",
389     "application/x-shockwave-flash": "flash",
390     "video/x-flv": "flash",
391     "audio/mpeg3": "media",
392     "audio/x-mpeg-3": "media",
393     "video/mpeg": "media",
394     "video/x-mpeg": "media",
395     "audio/ogg": "media",
396     "application/ogg": "media",
397     "application/x-ogg": "media",
398     "application/x-midi": "media",
399     "audio/midi": "media",
400     "audio/x-mid": "media",
401     "audio/x-midi": "media",
402     "music/crescendo": "media",
403     "audio/wav": "media",
404     "audio/x-wav": "media",
405     "text/json": "json",
406     "application/x-json": "json",
407     "application/json-rpc": "json",
408     "application/x-web-app-manifest+json": "json",
409   },
411   /**
412    * Check if the given MIME type is a text-only MIME type.
413    *
414    * @param string aMimeType
415    * @return boolean
416    */
417   isTextMimeType: function NH_isTextMimeType(aMimeType)
418   {
419     if (aMimeType.indexOf("text/") == 0) {
420       return true;
421     }
423     // XML and JSON often come with custom MIME types, so in addition to the
424     // standard "application/xml" and "application/json", we also look for
425     // variants like "application/x-bigcorp-xml" by checking for either string
426     // after any word boundary.
427     if (/^application\/[a-z-]+\b(xml|json)/.test(aMimeType)) {
428       return true;
429     }
431     switch (this.mimeCategoryMap[aMimeType]) {
432       case "txt":
433       case "js":
434       case "json":
435       case "css":
436       case "html":
437       case "svg":
438       case "xml":
439         return true;
441       default:
442         return false;
443     }
444   },
447 for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
448   exports[prop] = NetworkHelper[prop];