зеркало из https://github.com/mozilla/gecko-dev.git
Bug 803188 - Support converting between file paths and file:/// uris in OS.File. r=Yoric
This commit is contained in:
Родитель
675d61df9d
Коммит
9e5e3e01c8
|
@ -20,6 +20,7 @@
|
||||||
// Boilerplate used to be able to import this module both from the main
|
// Boilerplate used to be able to import this module both from the main
|
||||||
// thread and from worker threads.
|
// thread and from worker threads.
|
||||||
if (typeof Components != "undefined") {
|
if (typeof Components != "undefined") {
|
||||||
|
Components.utils.importGlobalProperties(["URL"]);
|
||||||
// Global definition of |exports|, to keep everybody happy.
|
// Global definition of |exports|, to keep everybody happy.
|
||||||
// In non-main thread, |exports| is provided by the module
|
// In non-main thread, |exports| is provided by the module
|
||||||
// loader.
|
// loader.
|
||||||
|
@ -33,7 +34,9 @@ let EXPORTED_SYMBOLS = [
|
||||||
"dirname",
|
"dirname",
|
||||||
"join",
|
"join",
|
||||||
"normalize",
|
"normalize",
|
||||||
"split"
|
"split",
|
||||||
|
"toFileURI",
|
||||||
|
"fromFileURI",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,6 +151,37 @@ let split = function(path) {
|
||||||
};
|
};
|
||||||
exports.split = split;
|
exports.split = split;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file:// URI file path of the given local file path.
|
||||||
|
*/
|
||||||
|
// The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
|
||||||
|
let toFileURIExtraEncodings = {';': '%3b', '?': '%3F', "'": '%27', '#': '%23'};
|
||||||
|
let toFileURI = function toFileURI(path) {
|
||||||
|
let uri = encodeURI(this.normalize(path));
|
||||||
|
|
||||||
|
// add a prefix, and encodeURI doesn't escape a few characters that we do
|
||||||
|
// want to escape, so fix that up
|
||||||
|
let prefix = "file://";
|
||||||
|
uri = prefix + uri.replace(/[;?'#]/g, match => toFileURIExtraEncodings[match]);
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
};
|
||||||
|
exports.toFileURI = toFileURI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local file path from a given file URI.
|
||||||
|
*/
|
||||||
|
let fromFileURI = function fromFileURI(uri) {
|
||||||
|
let url = new URL(uri);
|
||||||
|
if (url.protocol != 'file:') {
|
||||||
|
throw new Error("fromFileURI expects a file URI");
|
||||||
|
}
|
||||||
|
let path = this.normalize(decodeURIComponent(url.pathname));
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
exports.fromFileURI = fromFileURI;
|
||||||
|
|
||||||
|
|
||||||
//////////// Boilerplate
|
//////////// Boilerplate
|
||||||
if (typeof Components != "undefined") {
|
if (typeof Components != "undefined") {
|
||||||
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
// Boilerplate used to be able to import this module both from the main
|
// Boilerplate used to be able to import this module both from the main
|
||||||
// thread and from worker threads.
|
// thread and from worker threads.
|
||||||
if (typeof Components != "undefined") {
|
if (typeof Components != "undefined") {
|
||||||
|
Components.utils.importGlobalProperties(["URL"]);
|
||||||
// Global definition of |exports|, to keep everybody happy.
|
// Global definition of |exports|, to keep everybody happy.
|
||||||
// In non-main thread, |exports| is provided by the module
|
// In non-main thread, |exports| is provided by the module
|
||||||
// loader.
|
// loader.
|
||||||
|
@ -44,7 +45,9 @@ let EXPORTED_SYMBOLS = [
|
||||||
"normalize",
|
"normalize",
|
||||||
"split",
|
"split",
|
||||||
"winGetDrive",
|
"winGetDrive",
|
||||||
"winIsAbsolute"
|
"winIsAbsolute",
|
||||||
|
"toFileURI",
|
||||||
|
"fromFileURI",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,6 +290,57 @@ let split = function(path) {
|
||||||
};
|
};
|
||||||
exports.split = split;
|
exports.split = split;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the file:// URI file path of the given local file path.
|
||||||
|
*/
|
||||||
|
// The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
|
||||||
|
let toFileURIExtraEncodings = {';': '%3b', '?': '%3F', "'": '%27', '#': '%23'};
|
||||||
|
let toFileURI = function toFileURI(path) {
|
||||||
|
// URI-escape forward slashes and convert backward slashes to forward
|
||||||
|
path = this.normalize(path).replace(/[\\\/]/g, m => (m=='\\')? '/' : '%2F');
|
||||||
|
let uri = encodeURI(path);
|
||||||
|
|
||||||
|
// add a prefix, and encodeURI doesn't escape a few characters that we do
|
||||||
|
// want to escape, so fix that up
|
||||||
|
let prefix = "file:///";
|
||||||
|
uri = prefix + uri.replace(/[;?'#]/g, match => toFileURIExtraEncodings[match]);
|
||||||
|
|
||||||
|
// turn e.g., file:///C: into file:///C:/
|
||||||
|
if (uri.charAt(uri.length - 1) === ':') {
|
||||||
|
uri += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
};
|
||||||
|
exports.toFileURI = toFileURI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local file path from a given file URI.
|
||||||
|
*/
|
||||||
|
let fromFileURI = function fromFileURI(uri) {
|
||||||
|
let url = new URL(uri);
|
||||||
|
if (url.protocol != 'file:') {
|
||||||
|
throw new Error("fromFileURI expects a file URI");
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip leading slash, since Windows paths don't start with one
|
||||||
|
uri = url.pathname.substr(1);
|
||||||
|
|
||||||
|
let path = decodeURI(uri);
|
||||||
|
// decode a few characters where URL's parsing is overzealous
|
||||||
|
path = path.replace(/%(3b|3f|23)/gi,
|
||||||
|
match => decodeURIComponent(match));
|
||||||
|
path = this.normalize(path);
|
||||||
|
|
||||||
|
// this.normalize() does not remove the trailing slash if the path
|
||||||
|
// component is a drive letter. eg. 'C:\'' will not get normalized.
|
||||||
|
if (path.endsWith(":\\")) {
|
||||||
|
path = path.substr(0, path.length - 1);
|
||||||
|
}
|
||||||
|
return this.normalize(path);
|
||||||
|
};
|
||||||
|
exports.fromFileURI = fromFileURI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function: Remove any leading/trailing backslashes
|
* Utility function: Remove any leading/trailing backslashes
|
||||||
* from a string.
|
* from a string.
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
|
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
|
|
||||||
|
let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
|
||||||
|
|
||||||
|
// Test cases for filePathToURI
|
||||||
|
let paths = isWindows ? [
|
||||||
|
'C:\\',
|
||||||
|
'C:\\test',
|
||||||
|
'C:\\test\\',
|
||||||
|
'C:\\test%2f',
|
||||||
|
'C:\\test\\test\\test',
|
||||||
|
'C:\\test;+%',
|
||||||
|
'C:\\test?action=index\\',
|
||||||
|
'C:\\test\ test',
|
||||||
|
'\\\\C:\\a\\b\\c',
|
||||||
|
'\\\\Server\\a\\b\\c',
|
||||||
|
|
||||||
|
// note that per http://support.microsoft.com/kb/177506 (under more info),
|
||||||
|
// the following characters are allowed on Windows:
|
||||||
|
'C:\\char^',
|
||||||
|
'C:\\char&',
|
||||||
|
'C:\\char\'',
|
||||||
|
'C:\\char@',
|
||||||
|
'C:\\char{',
|
||||||
|
'C:\\char}',
|
||||||
|
'C:\\char[',
|
||||||
|
'C:\\char]',
|
||||||
|
'C:\\char,',
|
||||||
|
'C:\\char$',
|
||||||
|
'C:\\char=',
|
||||||
|
'C:\\char!',
|
||||||
|
'C:\\char-',
|
||||||
|
'C:\\char#',
|
||||||
|
'C:\\char(',
|
||||||
|
'C:\\char)',
|
||||||
|
'C:\\char%',
|
||||||
|
'C:\\char.',
|
||||||
|
'C:\\char+',
|
||||||
|
'C:\\char~',
|
||||||
|
'C:\\char_'
|
||||||
|
] : [
|
||||||
|
'/',
|
||||||
|
'/test',
|
||||||
|
'/test/',
|
||||||
|
'/test%2f',
|
||||||
|
'/test/test/test',
|
||||||
|
'/test;+%',
|
||||||
|
'/test?action=index/',
|
||||||
|
'/test\ test',
|
||||||
|
'/punctuation/;,/?:@&=+$-_.!~*\'()"#',
|
||||||
|
'/CasePreserving'
|
||||||
|
];
|
||||||
|
|
||||||
|
// some additional URIs to test, beyond those generated from paths
|
||||||
|
let uris = isWindows ? [
|
||||||
|
'file:///C:/test/',
|
||||||
|
'file://localhost/C:/test',
|
||||||
|
'file:///c:/test/test.txt',
|
||||||
|
//'file:///C:/foo%2f', // trailing, encoded slash
|
||||||
|
'file:///C:/%3f%3F',
|
||||||
|
'file:///C:/%3b%3B',
|
||||||
|
'file:///C:/%3c%3C', // not one of the special-cased ? or ;
|
||||||
|
'file:///C:/%78', // 'x', not usually uri encoded
|
||||||
|
'file:///C:/test#frag', // a fragment identifier
|
||||||
|
'file:///C:/test?action=index' // an actual query component
|
||||||
|
] : [
|
||||||
|
'file:///test/',
|
||||||
|
'file://localhost/test',
|
||||||
|
'file:///test/test.txt',
|
||||||
|
'file:///foo%2f', // trailing, encoded slash
|
||||||
|
'file:///%3f%3F',
|
||||||
|
'file:///%3b%3B',
|
||||||
|
'file:///%3c%3C', // not one of the special-cased ? or ;
|
||||||
|
'file:///%78', // 'x', not usually uri encoded
|
||||||
|
'file:///test#frag', // a fragment identifier
|
||||||
|
'file:///test?action=index' // an actual query component
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let path of paths) {
|
||||||
|
// convert that to a uri using FileUtils and Services, which toFileURI is trying to model
|
||||||
|
let file = FileUtils.File(path);
|
||||||
|
let uri = Services.io.newFileURI(file).spec;
|
||||||
|
do_check_eq(uri, OS.Path.toFileURI(path));
|
||||||
|
|
||||||
|
// keep the resulting URI to try the reverse
|
||||||
|
uris.push(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let uri of uris) {
|
||||||
|
// convert URIs to paths with nsIFileURI, which fromFileURI is trying to model
|
||||||
|
let path = Services.io.newURI(uri, null, null).QueryInterface(Components.interfaces.nsIFileURL).file.path;
|
||||||
|
do_check_eq(path, OS.Path.fromFileURI(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that non-file URLs aren't allowed
|
||||||
|
let thrown = false;
|
||||||
|
try {
|
||||||
|
OS.Path.fromFileURI('http://test.com')
|
||||||
|
} catch (e) {
|
||||||
|
do_check_eq(e.message, "fromFileURI expects a file URI");
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
do_check_true(thrown);
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ tail =
|
||||||
[test_removeEmptyDir.js]
|
[test_removeEmptyDir.js]
|
||||||
[test_makeDir.js]
|
[test_makeDir.js]
|
||||||
[test_profiledir.js]
|
[test_profiledir.js]
|
||||||
|
[test_file_URL_conversion.js]
|
||||||
[test_logging.js]
|
[test_logging.js]
|
||||||
[test_creationDate.js]
|
[test_creationDate.js]
|
||||||
[test_exception.js]
|
[test_exception.js]
|
||||||
|
|
Загрузка…
Ссылка в новой задаче