1 /* vim:set ts=2 sw=2 sts=2 et: */
3 * Software License Agreement (BSD License)
5 * Copyright (c) 2007, Parakey Inc.
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:
11 * * Redistributions of source code must retain the above
12 * copyright notice, this list of conditions and the
13 * following disclaimer.
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.
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.
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.
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)
47 * Mike Ratcliffe (Comartis AG)
48 * Hernan RodrÃguez Colmeiro
51 * Steven Roussey (AppCenter Inc, Network54)
52 * Mihai Sucan (Mozilla Corp.)
55 const {components, Cc, Ci, Cu} = require("chrome");
56 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
59 * Helper object for networking stuff.
61 * Most of the following functions have been taken from the Firebug source. They
62 * have been modified to match the Firefox coding rules.
66 * Converts aText with a given aCharset to unicode.
70 * @param string aCharset
71 * Charset to convert the text to.
75 convertToUnicode: function NH_convertToUnicode(aText, aCharset)
77 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
78 createInstance(Ci.nsIScriptableUnicodeConverter);
80 conv.charset = aCharset || "UTF-8";
81 return conv.ConvertToUnicode(aText);
89 * Reads all available bytes from aStream and converts them to aCharset.
91 * @param nsIInputStream aStream
92 * @param string aCharset
94 * UTF-16 encoded string based on the content of aStream and aCharset.
96 readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset)
100 text = NetUtil.readInputStreamToString(aStream, aStream.available())
101 return this.convertToUnicode(text, aCharset);
109 * Reads the posted text from aRequest.
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
118 readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset)
120 if (aRequest instanceof Ci.nsIUploadChannel) {
121 let iStream = aRequest.uploadStream;
123 let isSeekableStream = false;
124 if (iStream instanceof Ci.nsISeekableStream) {
125 isSeekableStream = true;
129 if (isSeekableStream) {
130 prevOffset = iStream.tell();
131 iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
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);
149 * Reads the posted text from the page's cache.
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.
157 readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset)
159 let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
160 return this.readPostTextFromPageViaWebNav(webNav, aCharset);
164 * Reads the posted text from the page's cache, given an nsIWebNavigation
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.
173 readPostTextFromPageViaWebNav:
174 function NH_readPostTextFromPageViaWebNav(aWebNav, aCharset)
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);
190 * Gets the nsIDOMWindow that is associated with aRequest.
192 * @param nsIHttpChannel aRequest
193 * @returns nsIDOMWindow or null
195 getWindowForRequest: function NH_getWindowForRequest(aRequest)
198 return this.getRequestLoadContext(aRequest).associatedWindow;
200 // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
201 // associatedWindow property.
207 * Gets the nsILoadContext that is associated with aRequest.
209 * @param nsIHttpChannel aRequest
210 * @returns nsILoadContext or null
212 getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
215 return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
219 return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
226 * Loads the content of aUrl from the cache.
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.
237 loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback)
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)) {
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));
263 * Parse a raw Cookie header value.
265 * @param string aHeader
266 * The raw Cookie header value.
268 * Array holding an object for each cookie. Each object holds the
269 * following properties: name and value.
271 parseCookieHeader: function NH_parseCookieHeader(aHeader)
273 let cookies = aHeader.split(";");
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())});
288 * Parse a raw Set-Cookie header value.
290 * @param string aHeader
291 * The raw Set-Cookie header value.
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).
297 parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader)
299 let rawCookies = aHeader.split(/\r\n|\n|\r/);
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;
315 else if (part.toLowerCase() == "httponly") {
316 cookie.httpOnly = true;
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];
324 else if (pair[0] == "expires") {
326 pair[1] = pair[1].replace(/-/g, ' ');
327 cookie.expires = new Date(pair[1]).toISOString();
334 cookies.push(cookie);
340 // This is a list of all the mime category maps jviereck could find in the
341 // firebug code base.
351 "text/x-setext": "txt",
352 "text/richtext": "txt",
353 "text/javascript": "js",
354 "text/jscript": "txt",
355 "text/tab-separated-values": "txt",
358 "text/ecmascript": "js",
359 "text/vnd.curl": "txt",
360 "text/x-json": "json",
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",
406 "application/x-json": "json",
407 "application/json-rpc": "json",
408 "application/x-web-app-manifest+json": "json",
412 * Check if the given MIME type is a text-only MIME type.
414 * @param string aMimeType
417 isTextMimeType: function NH_isTextMimeType(aMimeType)
419 if (aMimeType.indexOf("text/") == 0) {
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)) {
431 switch (this.mimeCategoryMap[aMimeType]) {
447 for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
448 exports[prop] = NetworkHelper[prop];