Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 */
35 /*
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 "use strict";
57 const {components, Cc, Ci, Cu} = require("chrome");
58 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
60 /**
61 * Helper object for networking stuff.
62 *
63 * Most of the following functions have been taken from the Firebug source. They
64 * have been modified to match the Firefox coding rules.
65 */
66 let NetworkHelper = {
67 /**
68 * Converts aText with a given aCharset to unicode.
69 *
70 * @param string aText
71 * Text to convert.
72 * @param string aCharset
73 * Charset to convert the text to.
74 * @returns string
75 * Converted text.
76 */
77 convertToUnicode: function NH_convertToUnicode(aText, aCharset)
78 {
79 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
80 createInstance(Ci.nsIScriptableUnicodeConverter);
81 try {
82 conv.charset = aCharset || "UTF-8";
83 return conv.ConvertToUnicode(aText);
84 }
85 catch (ex) {
86 return aText;
87 }
88 },
90 /**
91 * Reads all available bytes from aStream and converts them to aCharset.
92 *
93 * @param nsIInputStream aStream
94 * @param string aCharset
95 * @returns string
96 * UTF-16 encoded string based on the content of aStream and aCharset.
97 */
98 readAndConvertFromStream: function NH_readAndConvertFromStream(aStream, aCharset)
99 {
100 let text = null;
101 try {
102 text = NetUtil.readInputStreamToString(aStream, aStream.available())
103 return this.convertToUnicode(text, aCharset);
104 }
105 catch (err) {
106 return text;
107 }
108 },
110 /**
111 * Reads the posted text from aRequest.
112 *
113 * @param nsIHttpChannel aRequest
114 * @param string aCharset
115 * The content document charset, used when reading the POSTed data.
116 * @returns string or null
117 * Returns the posted string if it was possible to read from aRequest
118 * otherwise null.
119 */
120 readPostTextFromRequest: function NH_readPostTextFromRequest(aRequest, aCharset)
121 {
122 if (aRequest instanceof Ci.nsIUploadChannel) {
123 let iStream = aRequest.uploadStream;
125 let isSeekableStream = false;
126 if (iStream instanceof Ci.nsISeekableStream) {
127 isSeekableStream = true;
128 }
130 let prevOffset;
131 if (isSeekableStream) {
132 prevOffset = iStream.tell();
133 iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
134 }
136 // Read data from the stream.
137 let text = this.readAndConvertFromStream(iStream, aCharset);
139 // Seek locks the file, so seek to the beginning only if necko hasn't
140 // read it yet, since necko doesn't seek to 0 before reading (at lest
141 // not till 459384 is fixed).
142 if (isSeekableStream && prevOffset == 0) {
143 iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
144 }
145 return text;
146 }
147 return null;
148 },
150 /**
151 * Reads the posted text from the page's cache.
152 *
153 * @param nsIDocShell aDocShell
154 * @param string aCharset
155 * @returns string or null
156 * Returns the posted string if it was possible to read from
157 * aDocShell otherwise null.
158 */
159 readPostTextFromPage: function NH_readPostTextFromPage(aDocShell, aCharset)
160 {
161 let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
162 return this.readPostTextFromPageViaWebNav(webNav, aCharset);
163 },
165 /**
166 * Reads the posted text from the page's cache, given an nsIWebNavigation
167 * object.
168 *
169 * @param nsIWebNavigation aWebNav
170 * @param string aCharset
171 * @returns string or null
172 * Returns the posted string if it was possible to read from
173 * aWebNav, otherwise null.
174 */
175 readPostTextFromPageViaWebNav:
176 function NH_readPostTextFromPageViaWebNav(aWebNav, aCharset)
177 {
178 if (aWebNav instanceof Ci.nsIWebPageDescriptor) {
179 let descriptor = aWebNav.currentDescriptor;
181 if (descriptor instanceof Ci.nsISHEntry && descriptor.postData &&
182 descriptor instanceof Ci.nsISeekableStream) {
183 descriptor.seek(NS_SEEK_SET, 0);
185 return this.readAndConvertFromStream(descriptor, aCharset);
186 }
187 }
188 return null;
189 },
191 /**
192 * Gets the web appId that is associated with aRequest.
193 *
194 * @param nsIHttpChannel aRequest
195 * @returns number|null
196 * The appId for the given request, if available.
197 */
198 getAppIdForRequest: function NH_getAppIdForRequest(aRequest)
199 {
200 try {
201 return this.getRequestLoadContext(aRequest).appId;
202 } catch (ex) {
203 // request loadContent is not always available.
204 }
205 return null;
206 },
208 /**
209 * Gets the topFrameElement that is associated with aRequest.
210 *
211 * @param nsIHttpChannel aRequest
212 * @returns nsIDOMElement|null
213 * The top frame element for the given request, if available.
214 */
215 getTopFrameForRequest: function NH_getTopFrameForRequest(aRequest)
216 {
217 try {
218 return this.getRequestLoadContext(aRequest).topFrameElement;
219 } catch (ex) {
220 // request loadContent is not always available.
221 }
222 return null;
223 },
225 /**
226 * Gets the nsIDOMWindow that is associated with aRequest.
227 *
228 * @param nsIHttpChannel aRequest
229 * @returns nsIDOMWindow or null
230 */
231 getWindowForRequest: function NH_getWindowForRequest(aRequest)
232 {
233 try {
234 return this.getRequestLoadContext(aRequest).associatedWindow;
235 } catch (ex) {
236 // TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
237 // associatedWindow property.
238 }
239 return null;
240 },
242 /**
243 * Gets the nsILoadContext that is associated with aRequest.
244 *
245 * @param nsIHttpChannel aRequest
246 * @returns nsILoadContext or null
247 */
248 getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
249 {
250 try {
251 return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
252 } catch (ex) { }
254 try {
255 return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
256 } catch (ex) { }
258 return null;
259 },
261 /**
262 * Loads the content of aUrl from the cache.
263 *
264 * @param string aUrl
265 * URL to load the cached content for.
266 * @param string aCharset
267 * Assumed charset of the cached content. Used if there is no charset
268 * on the channel directly.
269 * @param function aCallback
270 * Callback that is called with the loaded cached content if available
271 * or null if something failed while getting the cached content.
272 */
273 loadFromCache: function NH_loadFromCache(aUrl, aCharset, aCallback)
274 {
275 let channel = NetUtil.newChannel(aUrl);
277 // Ensure that we only read from the cache and not the server.
278 channel.loadFlags = Ci.nsIRequest.LOAD_FROM_CACHE |
279 Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
280 Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
282 NetUtil.asyncFetch(channel, (aInputStream, aStatusCode, aRequest) => {
283 if (!components.isSuccessCode(aStatusCode)) {
284 aCallback(null);
285 return;
286 }
288 // Try to get the encoding from the channel. If there is none, then use
289 // the passed assumed aCharset.
290 let aChannel = aRequest.QueryInterface(Ci.nsIChannel);
291 let contentCharset = aChannel.contentCharset || aCharset;
293 // Read the content of the stream using contentCharset as encoding.
294 aCallback(this.readAndConvertFromStream(aInputStream, contentCharset));
295 });
296 },
298 /**
299 * Parse a raw Cookie header value.
300 *
301 * @param string aHeader
302 * The raw Cookie header value.
303 * @return array
304 * Array holding an object for each cookie. Each object holds the
305 * following properties: name and value.
306 */
307 parseCookieHeader: function NH_parseCookieHeader(aHeader)
308 {
309 let cookies = aHeader.split(";");
310 let result = [];
312 cookies.forEach(function(aCookie) {
313 let equal = aCookie.indexOf("=");
314 let name = aCookie.substr(0, equal);
315 let value = aCookie.substr(equal + 1);
316 result.push({name: unescape(name.trim()),
317 value: unescape(value.trim())});
318 });
320 return result;
321 },
323 /**
324 * Parse a raw Set-Cookie header value.
325 *
326 * @param string aHeader
327 * The raw Set-Cookie header value.
328 * @return array
329 * Array holding an object for each cookie. Each object holds the
330 * following properties: name, value, secure (boolean), httpOnly
331 * (boolean), path, domain and expires (ISO date string).
332 */
333 parseSetCookieHeader: function NH_parseSetCookieHeader(aHeader)
334 {
335 let rawCookies = aHeader.split(/\r\n|\n|\r/);
336 let cookies = [];
338 rawCookies.forEach(function(aCookie) {
339 let equal = aCookie.indexOf("=");
340 let name = unescape(aCookie.substr(0, equal).trim());
341 let parts = aCookie.substr(equal + 1).split(";");
342 let value = unescape(parts.shift().trim());
344 let cookie = {name: name, value: value};
346 parts.forEach(function(aPart) {
347 let part = aPart.trim();
348 if (part.toLowerCase() == "secure") {
349 cookie.secure = true;
350 }
351 else if (part.toLowerCase() == "httponly") {
352 cookie.httpOnly = true;
353 }
354 else if (part.indexOf("=") > -1) {
355 let pair = part.split("=");
356 pair[0] = pair[0].toLowerCase();
357 if (pair[0] == "path" || pair[0] == "domain") {
358 cookie[pair[0]] = pair[1];
359 }
360 else if (pair[0] == "expires") {
361 try {
362 pair[1] = pair[1].replace(/-/g, ' ');
363 cookie.expires = new Date(pair[1]).toISOString();
364 }
365 catch (ex) { }
366 }
367 }
368 });
370 cookies.push(cookie);
371 });
373 return cookies;
374 },
376 // This is a list of all the mime category maps jviereck could find in the
377 // firebug code base.
378 mimeCategoryMap: {
379 "text/plain": "txt",
380 "text/html": "html",
381 "text/xml": "xml",
382 "text/xsl": "txt",
383 "text/xul": "txt",
384 "text/css": "css",
385 "text/sgml": "txt",
386 "text/rtf": "txt",
387 "text/x-setext": "txt",
388 "text/richtext": "txt",
389 "text/javascript": "js",
390 "text/jscript": "txt",
391 "text/tab-separated-values": "txt",
392 "text/rdf": "txt",
393 "text/xif": "txt",
394 "text/ecmascript": "js",
395 "text/vnd.curl": "txt",
396 "text/x-json": "json",
397 "text/x-js": "txt",
398 "text/js": "txt",
399 "text/vbscript": "txt",
400 "view-source": "txt",
401 "view-fragment": "txt",
402 "application/xml": "xml",
403 "application/xhtml+xml": "xml",
404 "application/atom+xml": "xml",
405 "application/rss+xml": "xml",
406 "application/vnd.mozilla.maybe.feed": "xml",
407 "application/vnd.mozilla.xul+xml": "xml",
408 "application/javascript": "js",
409 "application/x-javascript": "js",
410 "application/x-httpd-php": "txt",
411 "application/rdf+xml": "xml",
412 "application/ecmascript": "js",
413 "application/http-index-format": "txt",
414 "application/json": "json",
415 "application/x-js": "txt",
416 "multipart/mixed": "txt",
417 "multipart/x-mixed-replace": "txt",
418 "image/svg+xml": "svg",
419 "application/octet-stream": "bin",
420 "image/jpeg": "image",
421 "image/jpg": "image",
422 "image/gif": "image",
423 "image/png": "image",
424 "image/bmp": "image",
425 "application/x-shockwave-flash": "flash",
426 "video/x-flv": "flash",
427 "audio/mpeg3": "media",
428 "audio/x-mpeg-3": "media",
429 "video/mpeg": "media",
430 "video/x-mpeg": "media",
431 "audio/ogg": "media",
432 "application/ogg": "media",
433 "application/x-ogg": "media",
434 "application/x-midi": "media",
435 "audio/midi": "media",
436 "audio/x-mid": "media",
437 "audio/x-midi": "media",
438 "music/crescendo": "media",
439 "audio/wav": "media",
440 "audio/x-wav": "media",
441 "text/json": "json",
442 "application/x-json": "json",
443 "application/json-rpc": "json",
444 "application/x-web-app-manifest+json": "json",
445 },
447 /**
448 * Check if the given MIME type is a text-only MIME type.
449 *
450 * @param string aMimeType
451 * @return boolean
452 */
453 isTextMimeType: function NH_isTextMimeType(aMimeType)
454 {
455 if (aMimeType.indexOf("text/") == 0) {
456 return true;
457 }
459 // XML and JSON often come with custom MIME types, so in addition to the
460 // standard "application/xml" and "application/json", we also look for
461 // variants like "application/x-bigcorp+xml". For JSON we allow "+json" and
462 // "-json" as suffixes.
463 if (/^application\/\w+(?:[\.-]\w+)*(?:\+xml|[-+]json)$/.test(aMimeType)) {
464 return true;
465 }
467 let category = this.mimeCategoryMap[aMimeType] || null;
468 switch (category) {
469 case "txt":
470 case "js":
471 case "json":
472 case "css":
473 case "html":
474 case "svg":
475 case "xml":
476 return true;
478 default:
479 return false;
480 }
481 },
482 };
484 for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
485 exports[prop] = NetworkHelper[prop];
486 }