Sat, 03 Jan 2015 20:18:00 +0100
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 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | "use strict"; |
michael@0 | 5 | |
michael@0 | 6 | module.metadata = { |
michael@0 | 7 | "stability": "experimental" |
michael@0 | 8 | }; |
michael@0 | 9 | |
michael@0 | 10 | const { Cc, Ci, Cr } = require("chrome"); |
michael@0 | 11 | |
michael@0 | 12 | const { Class } = require("./core/heritage"); |
michael@0 | 13 | const base64 = require("./base64"); |
michael@0 | 14 | var tlds = Cc["@mozilla.org/network/effective-tld-service;1"] |
michael@0 | 15 | .getService(Ci.nsIEffectiveTLDService); |
michael@0 | 16 | |
michael@0 | 17 | var ios = Cc['@mozilla.org/network/io-service;1'] |
michael@0 | 18 | .getService(Ci.nsIIOService); |
michael@0 | 19 | |
michael@0 | 20 | var resProt = ios.getProtocolHandler("resource") |
michael@0 | 21 | .QueryInterface(Ci.nsIResProtocolHandler); |
michael@0 | 22 | |
michael@0 | 23 | var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"] |
michael@0 | 24 | .getService(Ci.nsIURLParser); |
michael@0 | 25 | |
michael@0 | 26 | function newURI(uriStr, base) { |
michael@0 | 27 | try { |
michael@0 | 28 | let baseURI = base ? ios.newURI(base, null, null) : null; |
michael@0 | 29 | return ios.newURI(uriStr, null, baseURI); |
michael@0 | 30 | } |
michael@0 | 31 | catch (e) { |
michael@0 | 32 | if (e.result == Cr.NS_ERROR_MALFORMED_URI) { |
michael@0 | 33 | throw new Error("malformed URI: " + uriStr); |
michael@0 | 34 | } |
michael@0 | 35 | if (e.result == Cr.NS_ERROR_FAILURE || |
michael@0 | 36 | e.result == Cr.NS_ERROR_ILLEGAL_VALUE) { |
michael@0 | 37 | throw new Error("invalid URI: " + uriStr); |
michael@0 | 38 | } |
michael@0 | 39 | } |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | function resolveResourceURI(uri) { |
michael@0 | 43 | var resolved; |
michael@0 | 44 | try { |
michael@0 | 45 | resolved = resProt.resolveURI(uri); |
michael@0 | 46 | } |
michael@0 | 47 | catch (e) { |
michael@0 | 48 | if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) { |
michael@0 | 49 | throw new Error("resource does not exist: " + uri.spec); |
michael@0 | 50 | } |
michael@0 | 51 | } |
michael@0 | 52 | return resolved; |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | let fromFilename = exports.fromFilename = function fromFilename(path) { |
michael@0 | 56 | var file = Cc['@mozilla.org/file/local;1'] |
michael@0 | 57 | .createInstance(Ci.nsILocalFile); |
michael@0 | 58 | file.initWithPath(path); |
michael@0 | 59 | return ios.newFileURI(file).spec; |
michael@0 | 60 | }; |
michael@0 | 61 | |
michael@0 | 62 | let toFilename = exports.toFilename = function toFilename(url) { |
michael@0 | 63 | var uri = newURI(url); |
michael@0 | 64 | if (uri.scheme == "resource") |
michael@0 | 65 | uri = newURI(resolveResourceURI(uri)); |
michael@0 | 66 | if (uri.scheme == "chrome") { |
michael@0 | 67 | var channel = ios.newChannelFromURI(uri); |
michael@0 | 68 | try { |
michael@0 | 69 | channel = channel.QueryInterface(Ci.nsIFileChannel); |
michael@0 | 70 | return channel.file.path; |
michael@0 | 71 | } |
michael@0 | 72 | catch (e) { |
michael@0 | 73 | if (e.result == Cr.NS_NOINTERFACE) { |
michael@0 | 74 | throw new Error("chrome url isn't on filesystem: " + url); |
michael@0 | 75 | } |
michael@0 | 76 | } |
michael@0 | 77 | } |
michael@0 | 78 | if (uri.scheme == "file") { |
michael@0 | 79 | var file = uri.QueryInterface(Ci.nsIFileURL).file; |
michael@0 | 80 | return file.path; |
michael@0 | 81 | } |
michael@0 | 82 | throw new Error("cannot map to filename: " + url); |
michael@0 | 83 | }; |
michael@0 | 84 | |
michael@0 | 85 | function URL(url, base) { |
michael@0 | 86 | if (!(this instanceof URL)) { |
michael@0 | 87 | return new URL(url, base); |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | var uri = newURI(url, base); |
michael@0 | 91 | |
michael@0 | 92 | var userPass = null; |
michael@0 | 93 | try { |
michael@0 | 94 | userPass = uri.userPass ? uri.userPass : null; |
michael@0 | 95 | } |
michael@0 | 96 | catch (e) { |
michael@0 | 97 | if (e.result != Cr.NS_ERROR_FAILURE) { |
michael@0 | 98 | throw e; |
michael@0 | 99 | } |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | var host = null; |
michael@0 | 103 | try { |
michael@0 | 104 | host = uri.host; |
michael@0 | 105 | } |
michael@0 | 106 | catch (e) { |
michael@0 | 107 | if (e.result != Cr.NS_ERROR_FAILURE) { |
michael@0 | 108 | throw e; |
michael@0 | 109 | } |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | var port = null; |
michael@0 | 113 | try { |
michael@0 | 114 | port = uri.port == -1 ? null : uri.port; |
michael@0 | 115 | } |
michael@0 | 116 | catch (e) { |
michael@0 | 117 | if (e.result != Cr.NS_ERROR_FAILURE) { |
michael@0 | 118 | throw e; |
michael@0 | 119 | } |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}]; |
michael@0 | 123 | URLParser.parsePath.apply(URLParser, uriData); |
michael@0 | 124 | let [{ value: filepathPos }, { value: filepathLen }, |
michael@0 | 125 | { value: queryPos }, { value: queryLen }, |
michael@0 | 126 | { value: refPos }, { value: refLen }] = uriData.slice(2); |
michael@0 | 127 | |
michael@0 | 128 | let hash = uri.ref ? "#" + uri.ref : ""; |
michael@0 | 129 | let pathname = uri.path.substr(filepathPos, filepathLen); |
michael@0 | 130 | let search = uri.path.substr(queryPos, queryLen); |
michael@0 | 131 | search = search ? "?" + search : ""; |
michael@0 | 132 | |
michael@0 | 133 | this.__defineGetter__("scheme", function() uri.scheme); |
michael@0 | 134 | this.__defineGetter__("userPass", function() userPass); |
michael@0 | 135 | this.__defineGetter__("host", function() host); |
michael@0 | 136 | this.__defineGetter__("hostname", function() host); |
michael@0 | 137 | this.__defineGetter__("port", function() port); |
michael@0 | 138 | this.__defineGetter__("path", function() uri.path); |
michael@0 | 139 | this.__defineGetter__("pathname", function() pathname); |
michael@0 | 140 | this.__defineGetter__("hash", function() hash); |
michael@0 | 141 | this.__defineGetter__("href", function() uri.spec); |
michael@0 | 142 | this.__defineGetter__("origin", function() uri.prePath); |
michael@0 | 143 | this.__defineGetter__("protocol", function() uri.scheme + ":"); |
michael@0 | 144 | this.__defineGetter__("search", function() search); |
michael@0 | 145 | |
michael@0 | 146 | Object.defineProperties(this, { |
michael@0 | 147 | toString: { |
michael@0 | 148 | value: function URL_toString() new String(uri.spec).toString(), |
michael@0 | 149 | enumerable: false |
michael@0 | 150 | }, |
michael@0 | 151 | valueOf: { |
michael@0 | 152 | value: function() new String(uri.spec).valueOf(), |
michael@0 | 153 | enumerable: false |
michael@0 | 154 | }, |
michael@0 | 155 | toSource: { |
michael@0 | 156 | value: function() new String(uri.spec).toSource(), |
michael@0 | 157 | enumerable: false |
michael@0 | 158 | } |
michael@0 | 159 | }); |
michael@0 | 160 | |
michael@0 | 161 | return this; |
michael@0 | 162 | }; |
michael@0 | 163 | |
michael@0 | 164 | URL.prototype = Object.create(String.prototype); |
michael@0 | 165 | exports.URL = URL; |
michael@0 | 166 | |
michael@0 | 167 | /** |
michael@0 | 168 | * Parse and serialize a Data URL. |
michael@0 | 169 | * |
michael@0 | 170 | * See: http://tools.ietf.org/html/rfc2397 |
michael@0 | 171 | * |
michael@0 | 172 | * Note: Could be extended in the future to decode / encode automatically binary |
michael@0 | 173 | * data. |
michael@0 | 174 | */ |
michael@0 | 175 | const DataURL = Class({ |
michael@0 | 176 | |
michael@0 | 177 | get base64 () { |
michael@0 | 178 | return "base64" in this.parameters; |
michael@0 | 179 | }, |
michael@0 | 180 | |
michael@0 | 181 | set base64 (value) { |
michael@0 | 182 | if (value) |
michael@0 | 183 | this.parameters["base64"] = ""; |
michael@0 | 184 | else |
michael@0 | 185 | delete this.parameters["base64"]; |
michael@0 | 186 | }, |
michael@0 | 187 | /** |
michael@0 | 188 | * Initialize the Data URL object. If a uri is given, it will be parsed. |
michael@0 | 189 | * |
michael@0 | 190 | * @param {String} [uri] The uri to parse |
michael@0 | 191 | * |
michael@0 | 192 | * @throws {URIError} if the Data URL is malformed |
michael@0 | 193 | */ |
michael@0 | 194 | initialize: function(uri) { |
michael@0 | 195 | // Due to bug 751834 it is not possible document and define these |
michael@0 | 196 | // properties in the prototype. |
michael@0 | 197 | |
michael@0 | 198 | /** |
michael@0 | 199 | * An hashmap that contains the parameters of the Data URL. By default is |
michael@0 | 200 | * empty, that accordingly to RFC is equivalent to {"charset" : "US-ASCII"} |
michael@0 | 201 | */ |
michael@0 | 202 | this.parameters = {}; |
michael@0 | 203 | |
michael@0 | 204 | /** |
michael@0 | 205 | * The MIME type of the data. By default is empty, that accordingly to RFC |
michael@0 | 206 | * is equivalent to "text/plain" |
michael@0 | 207 | */ |
michael@0 | 208 | this.mimeType = ""; |
michael@0 | 209 | |
michael@0 | 210 | /** |
michael@0 | 211 | * The string that represent the data in the Data URL |
michael@0 | 212 | */ |
michael@0 | 213 | this.data = ""; |
michael@0 | 214 | |
michael@0 | 215 | if (typeof uri === "undefined") |
michael@0 | 216 | return; |
michael@0 | 217 | |
michael@0 | 218 | uri = String(uri); |
michael@0 | 219 | |
michael@0 | 220 | let matches = uri.match(/^data:([^,]*),(.*)$/i); |
michael@0 | 221 | |
michael@0 | 222 | if (!matches) |
michael@0 | 223 | throw new URIError("Malformed Data URL: " + uri); |
michael@0 | 224 | |
michael@0 | 225 | let mediaType = matches[1].trim(); |
michael@0 | 226 | |
michael@0 | 227 | this.data = decodeURIComponent(matches[2].trim()); |
michael@0 | 228 | |
michael@0 | 229 | if (!mediaType) |
michael@0 | 230 | return; |
michael@0 | 231 | |
michael@0 | 232 | let parametersList = mediaType.split(";"); |
michael@0 | 233 | |
michael@0 | 234 | this.mimeType = parametersList.shift().trim(); |
michael@0 | 235 | |
michael@0 | 236 | for (let parameter, i = 0; parameter = parametersList[i++];) { |
michael@0 | 237 | let pairs = parameter.split("="); |
michael@0 | 238 | let name = pairs[0].trim(); |
michael@0 | 239 | let value = pairs.length > 1 ? decodeURIComponent(pairs[1].trim()) : ""; |
michael@0 | 240 | |
michael@0 | 241 | this.parameters[name] = value; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | if (this.base64) |
michael@0 | 245 | this.data = base64.decode(this.data); |
michael@0 | 246 | |
michael@0 | 247 | }, |
michael@0 | 248 | |
michael@0 | 249 | /** |
michael@0 | 250 | * Returns the object as a valid Data URL string |
michael@0 | 251 | * |
michael@0 | 252 | * @returns {String} The Data URL |
michael@0 | 253 | */ |
michael@0 | 254 | toString : function() { |
michael@0 | 255 | let parametersList = []; |
michael@0 | 256 | |
michael@0 | 257 | for (let name in this.parameters) { |
michael@0 | 258 | let encodedParameter = encodeURIComponent(name); |
michael@0 | 259 | let value = this.parameters[name]; |
michael@0 | 260 | |
michael@0 | 261 | if (value) |
michael@0 | 262 | encodedParameter += "=" + encodeURIComponent(value); |
michael@0 | 263 | |
michael@0 | 264 | parametersList.push(encodedParameter); |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | // If there is at least a parameter, add an empty string in order |
michael@0 | 268 | // to start with a `;` on join call. |
michael@0 | 269 | if (parametersList.length > 0) |
michael@0 | 270 | parametersList.unshift(""); |
michael@0 | 271 | |
michael@0 | 272 | let data = this.base64 ? base64.encode(this.data) : this.data; |
michael@0 | 273 | |
michael@0 | 274 | return "data:" + |
michael@0 | 275 | this.mimeType + |
michael@0 | 276 | parametersList.join(";") + "," + |
michael@0 | 277 | encodeURIComponent(data); |
michael@0 | 278 | } |
michael@0 | 279 | }); |
michael@0 | 280 | |
michael@0 | 281 | exports.DataURL = DataURL; |
michael@0 | 282 | |
michael@0 | 283 | let getTLD = exports.getTLD = function getTLD (url) { |
michael@0 | 284 | let uri = newURI(url.toString()); |
michael@0 | 285 | let tld = null; |
michael@0 | 286 | try { |
michael@0 | 287 | tld = tlds.getPublicSuffix(uri); |
michael@0 | 288 | } |
michael@0 | 289 | catch (e) { |
michael@0 | 290 | if (e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS && |
michael@0 | 291 | e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS) { |
michael@0 | 292 | throw e; |
michael@0 | 293 | } |
michael@0 | 294 | } |
michael@0 | 295 | return tld; |
michael@0 | 296 | }; |
michael@0 | 297 | |
michael@0 | 298 | let isValidURI = exports.isValidURI = function (uri) { |
michael@0 | 299 | try { |
michael@0 | 300 | newURI(uri); |
michael@0 | 301 | } |
michael@0 | 302 | catch(e) { |
michael@0 | 303 | return false; |
michael@0 | 304 | } |
michael@0 | 305 | return true; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | function isLocalURL(url) { |
michael@0 | 309 | if (String.indexOf(url, './') === 0) |
michael@0 | 310 | return true; |
michael@0 | 311 | |
michael@0 | 312 | try { |
michael@0 | 313 | return ['resource', 'data', 'chrome'].indexOf(URL(url).scheme) > -1; |
michael@0 | 314 | } |
michael@0 | 315 | catch(e) {} |
michael@0 | 316 | |
michael@0 | 317 | return false; |
michael@0 | 318 | } |
michael@0 | 319 | exports.isLocalURL = isLocalURL; |