toolkit/devtools/webconsole/network-helper.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial