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.

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

mercurial