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 | /** |
michael@0 | 2 | * Handling native paths. |
michael@0 | 3 | * |
michael@0 | 4 | * This module contains a number of functions destined to simplify |
michael@0 | 5 | * working with native paths through a cross-platform API. Functions |
michael@0 | 6 | * of this module will only work with the following assumptions: |
michael@0 | 7 | * |
michael@0 | 8 | * - paths are valid; |
michael@0 | 9 | * - paths are defined with one of the grammars that this module can |
michael@0 | 10 | * parse (see later); |
michael@0 | 11 | * - all path concatenations go through function |join|. |
michael@0 | 12 | * |
michael@0 | 13 | * Limitations of this implementation. |
michael@0 | 14 | * |
michael@0 | 15 | * Windows supports 6 distinct grammars for paths. For the moment, this |
michael@0 | 16 | * implementation supports the following subset: |
michael@0 | 17 | * |
michael@0 | 18 | * - drivename:backslash-separated components |
michael@0 | 19 | * - backslash-separated components |
michael@0 | 20 | * - \\drivename\ followed by backslash-separated components |
michael@0 | 21 | * |
michael@0 | 22 | * Additionally, |normalize| can convert a path containing slash- |
michael@0 | 23 | * separated components to a path containing backslash-separated |
michael@0 | 24 | * components. |
michael@0 | 25 | */ |
michael@0 | 26 | |
michael@0 | 27 | "use strict"; |
michael@0 | 28 | |
michael@0 | 29 | // Boilerplate used to be able to import this module both from the main |
michael@0 | 30 | // thread and from worker threads. |
michael@0 | 31 | if (typeof Components != "undefined") { |
michael@0 | 32 | Components.utils.importGlobalProperties(["URL"]); |
michael@0 | 33 | // Global definition of |exports|, to keep everybody happy. |
michael@0 | 34 | // In non-main thread, |exports| is provided by the module |
michael@0 | 35 | // loader. |
michael@0 | 36 | this.exports = {}; |
michael@0 | 37 | } else if (typeof "module" == "undefined" || typeof "exports" == "undefined") { |
michael@0 | 38 | throw new Error("Please load this module using require()"); |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | let EXPORTED_SYMBOLS = [ |
michael@0 | 42 | "basename", |
michael@0 | 43 | "dirname", |
michael@0 | 44 | "join", |
michael@0 | 45 | "normalize", |
michael@0 | 46 | "split", |
michael@0 | 47 | "winGetDrive", |
michael@0 | 48 | "winIsAbsolute", |
michael@0 | 49 | "toFileURI", |
michael@0 | 50 | "fromFileURI", |
michael@0 | 51 | ]; |
michael@0 | 52 | |
michael@0 | 53 | /** |
michael@0 | 54 | * Return the final part of the path. |
michael@0 | 55 | * The final part of the path is everything after the last "\\". |
michael@0 | 56 | */ |
michael@0 | 57 | let basename = function(path) { |
michael@0 | 58 | if (path.startsWith("\\\\")) { |
michael@0 | 59 | // UNC-style path |
michael@0 | 60 | let index = path.lastIndexOf("\\"); |
michael@0 | 61 | if (index != 1) { |
michael@0 | 62 | return path.slice(index + 1); |
michael@0 | 63 | } |
michael@0 | 64 | return ""; // Degenerate case |
michael@0 | 65 | } |
michael@0 | 66 | return path.slice(Math.max(path.lastIndexOf("\\"), |
michael@0 | 67 | path.lastIndexOf(":")) + 1); |
michael@0 | 68 | }; |
michael@0 | 69 | exports.basename = basename; |
michael@0 | 70 | |
michael@0 | 71 | /** |
michael@0 | 72 | * Return the directory part of the path. |
michael@0 | 73 | * |
michael@0 | 74 | * If the path contains no directory, return the drive letter, |
michael@0 | 75 | * or "." if the path contains no drive letter or if option |
michael@0 | 76 | * |winNoDrive| is set. |
michael@0 | 77 | * |
michael@0 | 78 | * Otherwise, return everything before the last backslash, |
michael@0 | 79 | * including the drive/server name. |
michael@0 | 80 | * |
michael@0 | 81 | * |
michael@0 | 82 | * @param {string} path The path. |
michael@0 | 83 | * @param {*=} options Platform-specific options controlling the behavior |
michael@0 | 84 | * of this function. This implementation supports the following options: |
michael@0 | 85 | * - |winNoDrive| If |true|, also remove the letter from the path name. |
michael@0 | 86 | */ |
michael@0 | 87 | let dirname = function(path, options) { |
michael@0 | 88 | let noDrive = (options && options.winNoDrive); |
michael@0 | 89 | |
michael@0 | 90 | // Find the last occurrence of "\\" |
michael@0 | 91 | let index = path.lastIndexOf("\\"); |
michael@0 | 92 | if (index == -1) { |
michael@0 | 93 | // If there is no directory component... |
michael@0 | 94 | if (!noDrive) { |
michael@0 | 95 | // Return the drive path if possible, falling back to "." |
michael@0 | 96 | return this.winGetDrive(path) || "."; |
michael@0 | 97 | } else { |
michael@0 | 98 | // Or just "." |
michael@0 | 99 | return "."; |
michael@0 | 100 | } |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | if (index == 1 && path.charAt(0) == "\\") { |
michael@0 | 104 | // The path is reduced to a UNC drive |
michael@0 | 105 | if (noDrive) { |
michael@0 | 106 | return "."; |
michael@0 | 107 | } else { |
michael@0 | 108 | return path; |
michael@0 | 109 | } |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | // Ignore any occurrence of "\\: immediately before that one |
michael@0 | 113 | while (index >= 0 && path[index] == "\\") { |
michael@0 | 114 | --index; |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | // Compute what is left, removing the drive name if necessary |
michael@0 | 118 | let start; |
michael@0 | 119 | if (noDrive) { |
michael@0 | 120 | start = (this.winGetDrive(path) || "").length; |
michael@0 | 121 | } else { |
michael@0 | 122 | start = 0; |
michael@0 | 123 | } |
michael@0 | 124 | return path.slice(start, index + 1); |
michael@0 | 125 | }; |
michael@0 | 126 | exports.dirname = dirname; |
michael@0 | 127 | |
michael@0 | 128 | /** |
michael@0 | 129 | * Join path components. |
michael@0 | 130 | * This is the recommended manner of getting the path of a file/subdirectory |
michael@0 | 131 | * in a directory. |
michael@0 | 132 | * |
michael@0 | 133 | * Example: Obtaining $TMP/foo/bar in an OS-independent manner |
michael@0 | 134 | * var tmpDir = OS.Constants.Path.tmpDir; |
michael@0 | 135 | * var path = OS.Path.join(tmpDir, "foo", "bar"); |
michael@0 | 136 | * |
michael@0 | 137 | * Under Windows, this will return "$TMP\foo\bar". |
michael@0 | 138 | */ |
michael@0 | 139 | let join = function(...path) { |
michael@0 | 140 | let paths = []; |
michael@0 | 141 | let root; |
michael@0 | 142 | let absolute = false; |
michael@0 | 143 | for (let subpath of path) { |
michael@0 | 144 | if (subpath == null) { |
michael@0 | 145 | throw new TypeError("invalid path component"); |
michael@0 | 146 | } |
michael@0 | 147 | let drive = this.winGetDrive(subpath); |
michael@0 | 148 | if (drive) { |
michael@0 | 149 | root = drive; |
michael@0 | 150 | let component = trimBackslashes(subpath.slice(drive.length)); |
michael@0 | 151 | if (component) { |
michael@0 | 152 | paths = [component]; |
michael@0 | 153 | } else { |
michael@0 | 154 | paths = []; |
michael@0 | 155 | } |
michael@0 | 156 | absolute = true; |
michael@0 | 157 | } else if (this.winIsAbsolute(subpath)) { |
michael@0 | 158 | paths = [trimBackslashes(subpath)]; |
michael@0 | 159 | absolute = true; |
michael@0 | 160 | } else { |
michael@0 | 161 | paths.push(trimBackslashes(subpath)); |
michael@0 | 162 | } |
michael@0 | 163 | } |
michael@0 | 164 | let result = ""; |
michael@0 | 165 | if (root) { |
michael@0 | 166 | result += root; |
michael@0 | 167 | } |
michael@0 | 168 | if (absolute) { |
michael@0 | 169 | result += "\\"; |
michael@0 | 170 | } |
michael@0 | 171 | result += paths.join("\\"); |
michael@0 | 172 | return result; |
michael@0 | 173 | }; |
michael@0 | 174 | exports.join = join; |
michael@0 | 175 | |
michael@0 | 176 | /** |
michael@0 | 177 | * Return the drive name of a path, or |null| if the path does |
michael@0 | 178 | * not contain a drive name. |
michael@0 | 179 | * |
michael@0 | 180 | * Drive name appear either as "DriveName:..." (the return drive |
michael@0 | 181 | * name includes the ":") or "\\\\DriveName..." (the returned drive name |
michael@0 | 182 | * includes "\\\\"). |
michael@0 | 183 | */ |
michael@0 | 184 | let winGetDrive = function(path) { |
michael@0 | 185 | if (path == null) { |
michael@0 | 186 | throw new TypeError("path is invalid"); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | if (path.startsWith("\\\\")) { |
michael@0 | 190 | // UNC path |
michael@0 | 191 | if (path.length == 2) { |
michael@0 | 192 | return null; |
michael@0 | 193 | } |
michael@0 | 194 | let index = path.indexOf("\\", 2); |
michael@0 | 195 | if (index == -1) { |
michael@0 | 196 | return path; |
michael@0 | 197 | } |
michael@0 | 198 | return path.slice(0, index); |
michael@0 | 199 | } |
michael@0 | 200 | // Non-UNC path |
michael@0 | 201 | let index = path.indexOf(":"); |
michael@0 | 202 | if (index <= 0) return null; |
michael@0 | 203 | return path.slice(0, index + 1); |
michael@0 | 204 | }; |
michael@0 | 205 | exports.winGetDrive = winGetDrive; |
michael@0 | 206 | |
michael@0 | 207 | /** |
michael@0 | 208 | * Return |true| if the path is absolute, |false| otherwise. |
michael@0 | 209 | * |
michael@0 | 210 | * We consider that a path is absolute if it starts with "\\" |
michael@0 | 211 | * or "driveletter:\\". |
michael@0 | 212 | */ |
michael@0 | 213 | let winIsAbsolute = function(path) { |
michael@0 | 214 | let index = path.indexOf(":"); |
michael@0 | 215 | return path.length > index + 1 && path[index + 1] == "\\"; |
michael@0 | 216 | }; |
michael@0 | 217 | exports.winIsAbsolute = winIsAbsolute; |
michael@0 | 218 | |
michael@0 | 219 | /** |
michael@0 | 220 | * Normalize a path by removing any unneeded ".", "..", "\\". |
michael@0 | 221 | * Also convert any "/" to a "\\". |
michael@0 | 222 | */ |
michael@0 | 223 | let normalize = function(path) { |
michael@0 | 224 | let stack = []; |
michael@0 | 225 | |
michael@0 | 226 | if (!path.startsWith("\\\\")) { |
michael@0 | 227 | // Normalize "/" to "\\" |
michael@0 | 228 | path = path.replace(/\//g, "\\"); |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | // Remove the drive (we will put it back at the end) |
michael@0 | 232 | let root = this.winGetDrive(path); |
michael@0 | 233 | if (root) { |
michael@0 | 234 | path = path.slice(root.length); |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | // Remember whether we need to restore a leading "\\" or drive name. |
michael@0 | 238 | let absolute = this.winIsAbsolute(path); |
michael@0 | 239 | |
michael@0 | 240 | // And now, fill |stack| from the components, |
michael@0 | 241 | // popping whenever there is a ".." |
michael@0 | 242 | path.split("\\").forEach(function loop(v) { |
michael@0 | 243 | switch (v) { |
michael@0 | 244 | case "": case ".": // Ignore |
michael@0 | 245 | break; |
michael@0 | 246 | case "..": |
michael@0 | 247 | if (stack.length == 0) { |
michael@0 | 248 | if (absolute) { |
michael@0 | 249 | throw new Error("Path is ill-formed: attempting to go past root"); |
michael@0 | 250 | } else { |
michael@0 | 251 | stack.push(".."); |
michael@0 | 252 | } |
michael@0 | 253 | } else { |
michael@0 | 254 | if (stack[stack.length - 1] == "..") { |
michael@0 | 255 | stack.push(".."); |
michael@0 | 256 | } else { |
michael@0 | 257 | stack.pop(); |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | break; |
michael@0 | 261 | default: |
michael@0 | 262 | stack.push(v); |
michael@0 | 263 | } |
michael@0 | 264 | }); |
michael@0 | 265 | |
michael@0 | 266 | // Put everything back together |
michael@0 | 267 | let result = stack.join("\\"); |
michael@0 | 268 | if (absolute || root) { |
michael@0 | 269 | result = "\\" + result; |
michael@0 | 270 | } |
michael@0 | 271 | if (root) { |
michael@0 | 272 | result = root + result; |
michael@0 | 273 | } |
michael@0 | 274 | return result; |
michael@0 | 275 | }; |
michael@0 | 276 | exports.normalize = normalize; |
michael@0 | 277 | |
michael@0 | 278 | /** |
michael@0 | 279 | * Return the components of a path. |
michael@0 | 280 | * You should generally apply this function to a normalized path. |
michael@0 | 281 | * |
michael@0 | 282 | * @return {{ |
michael@0 | 283 | * {bool} absolute |true| if the path is absolute, |false| otherwise |
michael@0 | 284 | * {array} components the string components of the path |
michael@0 | 285 | * {string?} winDrive the drive or server for this path |
michael@0 | 286 | * }} |
michael@0 | 287 | * |
michael@0 | 288 | * Other implementations may add additional OS-specific informations. |
michael@0 | 289 | */ |
michael@0 | 290 | let split = function(path) { |
michael@0 | 291 | return { |
michael@0 | 292 | absolute: this.winIsAbsolute(path), |
michael@0 | 293 | winDrive: this.winGetDrive(path), |
michael@0 | 294 | components: path.split("\\") |
michael@0 | 295 | }; |
michael@0 | 296 | }; |
michael@0 | 297 | exports.split = split; |
michael@0 | 298 | |
michael@0 | 299 | /** |
michael@0 | 300 | * Return the file:// URI file path of the given local file path. |
michael@0 | 301 | */ |
michael@0 | 302 | // The case of %3b is designed to match Services.io, but fundamentally doesn't matter. |
michael@0 | 303 | let toFileURIExtraEncodings = {';': '%3b', '?': '%3F', "'": '%27', '#': '%23'}; |
michael@0 | 304 | let toFileURI = function toFileURI(path) { |
michael@0 | 305 | // URI-escape forward slashes and convert backward slashes to forward |
michael@0 | 306 | path = this.normalize(path).replace(/[\\\/]/g, m => (m=='\\')? '/' : '%2F'); |
michael@0 | 307 | let uri = encodeURI(path); |
michael@0 | 308 | |
michael@0 | 309 | // add a prefix, and encodeURI doesn't escape a few characters that we do |
michael@0 | 310 | // want to escape, so fix that up |
michael@0 | 311 | let prefix = "file:///"; |
michael@0 | 312 | uri = prefix + uri.replace(/[;?'#]/g, match => toFileURIExtraEncodings[match]); |
michael@0 | 313 | |
michael@0 | 314 | // turn e.g., file:///C: into file:///C:/ |
michael@0 | 315 | if (uri.charAt(uri.length - 1) === ':') { |
michael@0 | 316 | uri += "/" |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | return uri; |
michael@0 | 320 | }; |
michael@0 | 321 | exports.toFileURI = toFileURI; |
michael@0 | 322 | |
michael@0 | 323 | /** |
michael@0 | 324 | * Returns the local file path from a given file URI. |
michael@0 | 325 | */ |
michael@0 | 326 | let fromFileURI = function fromFileURI(uri) { |
michael@0 | 327 | let url = new URL(uri); |
michael@0 | 328 | if (url.protocol != 'file:') { |
michael@0 | 329 | throw new Error("fromFileURI expects a file URI"); |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | // strip leading slash, since Windows paths don't start with one |
michael@0 | 333 | uri = url.pathname.substr(1); |
michael@0 | 334 | |
michael@0 | 335 | let path = decodeURI(uri); |
michael@0 | 336 | // decode a few characters where URL's parsing is overzealous |
michael@0 | 337 | path = path.replace(/%(3b|3f|23)/gi, |
michael@0 | 338 | match => decodeURIComponent(match)); |
michael@0 | 339 | path = this.normalize(path); |
michael@0 | 340 | |
michael@0 | 341 | // this.normalize() does not remove the trailing slash if the path |
michael@0 | 342 | // component is a drive letter. eg. 'C:\'' will not get normalized. |
michael@0 | 343 | if (path.endsWith(":\\")) { |
michael@0 | 344 | path = path.substr(0, path.length - 1); |
michael@0 | 345 | } |
michael@0 | 346 | return this.normalize(path); |
michael@0 | 347 | }; |
michael@0 | 348 | exports.fromFileURI = fromFileURI; |
michael@0 | 349 | |
michael@0 | 350 | /** |
michael@0 | 351 | * Utility function: Remove any leading/trailing backslashes |
michael@0 | 352 | * from a string. |
michael@0 | 353 | */ |
michael@0 | 354 | let trimBackslashes = function trimBackslashes(string) { |
michael@0 | 355 | return string.replace(/^\\+|\\+$/g,''); |
michael@0 | 356 | }; |
michael@0 | 357 | |
michael@0 | 358 | //////////// Boilerplate |
michael@0 | 359 | if (typeof Components != "undefined") { |
michael@0 | 360 | this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; |
michael@0 | 361 | for (let symbol of EXPORTED_SYMBOLS) { |
michael@0 | 362 | this[symbol] = exports[symbol]; |
michael@0 | 363 | } |
michael@0 | 364 | } |