toolkit/components/osfile/modules/ospath_win.jsm

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 /**
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 }

mercurial