gecko/toolkit/components/osfile/osfile_win_back.jsm
2012-07-05 10:21:09 +02:00

567 lines
20 KiB
JavaScript

/* 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/. */
/**
* This file can be used in the following contexts:
*
* 1. included from a non-osfile worker thread using importScript
* (it serves to define a synchronous API for that worker thread)
* (bug 707681)
*
* 2. included from the main thread using Components.utils.import
* (it serves to define the asynchronous API, whose implementation
* resides in the worker thread)
* (bug 729057)
*
* 3. included from the osfile worker thread using importScript
* (it serves to define the implementation of the asynchronous API)
* (bug 729057)
*/
{
if (typeof Components != "undefined") {
// We do not wish osfile_win.jsm to be used directly as a main thread
// module yet. When time comes, it will be loaded by a combination of
// a main thread front-end/worker thread implementation that makes sure
// that we are not executing synchronous IO code in the main thread.
throw new Error("osfile_win.jsm cannot be used from the main thread yet");
}
importScripts("resource://gre/modules/osfile/osfile_shared.jsm");
(function(exports) {
"use strict";
if (!exports.OS) {
exports.OS = {};
}
if (!exports.OS.Win) {
exports.OS.Win = {};
}
if (exports.OS.Win.File) {
return; // Avoid double-initialization
}
exports.OS.Win.File = {};
let LOG = OS.Shared.LOG.bind(OS.Shared, "OS.Win.File");
let libc = ctypes.open("kernel32.dll");
if (!libc) {
throw new Error("Could not open kernel32.dll");
}
/**
* Initialize the Windows module.
*
* @param {function=} declareFFI
*/
let init = function init(aDeclareFFI) {
let declareFFI;
if (aDeclareFFI) {
declareFFI = aDeclareFFI.bind(null, libc);
} else {
declareFFI = OS.Shared.declareFFI.bind(null, libc);
}
// Shorthands
let OSWin = exports.OS.Win;
let WinFile = exports.OS.Win.File;
if (!exports.OS.Types) {
exports.OS.Types = {};
}
let Type = exports.OS.Shared.Type;
let Types = Type;
// Initialize types
Types.HANDLE =
new Type("HANDLE",
ctypes.voidptr_t);
/**
* A C integer holding INVALID_HANDLE_VALUE in case of error or
* a file descriptor in case of success.
*/
Types.maybe_HANDLE =
new Type("maybe_HANDLE",
Types.HANDLE.implementation,
function (maybe) {
if (ctypes.cast(maybe, ctypes.int).value == invalid_handle) {
// Ensure that API clients can effectively compare against
// Const.INVALID_HANDLE_VALUE. Without this cast,
// == would always return |false|.
return invalid_handle;
}
return ctypes.CDataFinalizer(maybe, _CloseHandle);
});
/**
* A C integer holding INVALID_HANDLE_VALUE in case of error or
* a file descriptor in case of success.
*/
Types.maybe_find_HANDLE =
new Type("maybe_find_HANDLE",
Types.HANDLE.implementation,
function (maybe) {
if (ctypes.cast(maybe, ctypes.int).value == invalid_handle) {
// Ensure that API clients can effectively compare against
// Const.INVALID_HANDLE_VALUE. Without this cast,
// == would always return |false|.
return invalid_handle;
}
return ctypes.CDataFinalizer(maybe, _FindClose);
});
let invalid_handle = exports.OS.Constants.Win.INVALID_HANDLE_VALUE;
Types.DWORD = Types.int32_t;
/**
* A C integer holding -1 in case of error or a positive integer
* in case of success.
*/
Types.negative_or_DWORD =
new Type("negative_or_DWORD",
ctypes.int32_t);
/**
* A C integer holding 0 in case of error or a positive integer
* in case of success.
*/
Types.zero_or_DWORD =
new Type("zero_or_DWORD",
ctypes.int32_t);
/**
* A C integer holding 0 in case of error, any other value in
* case of success.
*/
Types.zero_or_nothing =
new Type("zero_or_nothing",
Types.bool.implementation);
Types.FILETIME =
new Type("FILETIME",
ctypes.StructType("FILETIME", [
{ lo: Types.DWORD.implementation },
{ hi: Types.DWORD.implementation }]));
Types.FindData =
new Type("FIND_DATA",
ctypes.StructType("FIND_DATA", [
{ dwFileAttributes: ctypes.uint32_t },
{ ftCreationTime: Types.FILETIME.implementation },
{ ftLastAccessTime: Types.FILETIME.implementation },
{ ftLastWriteTime: Types.FILETIME.implementation },
{ nFileSizeHigh: Types.DWORD.implementation },
{ nFileSizeLow: Types.DWORD.implementation },
{ dwReserved0: Types.DWORD.implementation },
{ dwReserved1: Types.DWORD.implementation },
{ cFileName: ctypes.ArrayType(ctypes.jschar, exports.OS.Constants.Win.MAX_PATH) },
{ cAlternateFileName: ctypes.ArrayType(ctypes.jschar, 14) }
]));
Types.SystemTime =
new Type("SystemTime",
ctypes.StructType("SystemTime", [
{ wYear: ctypes.int16_t },
{ wMonth: ctypes.int16_t },
{ wDayOfWeek: ctypes.int16_t },
{ wDay: ctypes.int16_t },
{ wHour: ctypes.int16_t },
{ wMinute: ctypes.int16_t },
{ wSecond: ctypes.int16_t },
{ wMilliSeconds: ctypes.int16_t }
]));
// Special case: these functions are used by the
// finalizer
let _CloseHandle =
libc.declare("CloseHandle", ctypes.winapi_abi,
/*return */ctypes.bool,
/*handle*/ ctypes.voidptr_t);
WinFile.CloseHandle = function(fd) {
return fd.dispose(); // Returns the value of |CloseHandle|.
};
let _FindClose =
libc.declare("CloseHandle", ctypes.winapi_abi,
/*return */ctypes.bool,
/*handle*/ ctypes.voidptr_t);
WinFile.FindClose = function(handle) {
return handle.dispose(); // Returns the value of |CloseHandle|.
};
// Declare libc functions as functions of |OS.Win.File|
WinFile.CopyFile =
declareFFI("CopyFileW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*sourcePath*/ Types.jschar.in_ptr,
/*destPath*/ Types.jschar.in_ptr,
/*bailIfExist*/Types.bool);
WinFile.CreateFile =
declareFFI("CreateFileW", ctypes.winapi_abi,
/*return*/ Types.maybe_HANDLE,
/*name*/ Types.jschar.in_ptr,
/*access*/ Types.DWORD,
/*share*/ Types.DWORD,
/*security*/Types.void_t.in_ptr,// FIXME: Implement?
/*creation*/Types.DWORD,
/*flags*/ Types.DWORD,
/*template*/Types.HANDLE);
WinFile.DeleteFile =
declareFFI("DeleteFileW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*path*/ Types.jschar.in_ptr);
WinFile.FileTimeToSystemTime =
declareFFI("FileTimeToSystemTime", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*filetime*/Types.FILETIME.in_ptr,
/*systime*/ Types.SystemTime.out_ptr);
WinFile.FindFirstFile =
declareFFI("FindFirstFileW", ctypes.winapi_abi,
/*return*/ Types.maybe_find_HANDLE,
/*pattern*/Types.jschar.in_ptr,
/*data*/ Types.FindData.out_ptr);
WinFile.FindNextFile =
declareFFI("FindNextFileW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*prev*/ Types.HANDLE,
/*data*/ Types.FindData.out_ptr);
WinFile.FormatMessage =
declareFFI("FormatMessageW", ctypes.winapi_abi,
/*return*/ Types.DWORD,
/*flags*/ Types.DWORD,
/*source*/ Types.void_t.in_ptr,
/*msgid*/ Types.DWORD,
/*langid*/ Types.DWORD,
/*buf*/ Types.jschar.out_ptr,
/*size*/ Types.DWORD,
/*Arguments*/Types.void_t.in_ptr
);
WinFile.GetCurrentDirectory =
declareFFI("GetCurrentDirectoryW", ctypes.winapi_abi,
/*return*/ Types.zero_or_DWORD,
/*length*/ Types.DWORD,
/*buf*/ Types.jschar.out_ptr
);
WinFile.MoveFileEx =
declareFFI("MoveFileExW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*sourcePath*/ Types.jschar.in_ptr,
/*destPath*/ Types.jschar.in_ptr,
/*flags*/ Types.DWORD
);
WinFile.ReadFile =
declareFFI("ReadFile", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*file*/ Types.HANDLE,
/*buffer*/ Types.char.out_ptr,
/*nbytes*/ Types.DWORD,
/*nbytes_read*/Types.DWORD.out_ptr,
/*overlapped*/Types.void_t.inout_ptr // FIXME: Implement?
);
WinFile.RemoveDirectory =
declareFFI("RemoveDirectoryW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*path*/ Types.jschar.in_ptr);
WinFile.SetCurrentDirectory =
declareFFI("SetCurrentDirectoryW", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*path*/ Types.jschar.in_ptr
);
WinFile.SetEndOfFile =
declareFFI("SetEndOfFile", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*file*/ Types.HANDLE);
WinFile.SetFilePointer =
declareFFI("SetFilePointer", ctypes.winapi_abi,
/*return*/ Types.negative_or_DWORD,
/*file*/ Types.HANDLE,
/*distlow*/Types.long,
/*disthi*/ Types.long.in_ptr,
/*method*/ Types.DWORD);
WinFile.WriteFile =
declareFFI("WriteFile", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*file*/ Types.HANDLE,
/*buffer*/ Types.char.in_ptr,
/*nbytes*/ Types.DWORD,
/*nbytes_wr*/Types.DWORD.out_ptr,
/*overlapped*/Types.void_t.inout_ptr // FIXME: Implement?
);
/**
* Utility function: check that a path is not a UNC path,
* as the current implementation of |Path| does not support
* UNC grammars.
*
* We consider that any path starting with "\\\\" is a UNC path.
*/
let ensureNotUNC = function ensureNotUNC(path) {
if (!path) {
throw new TypeError("Expecting a non-null path");
}
if (path.length >= 2 && path[0] == "\\" && path[1] == "\\") {
throw new Error("Module Path cannot handle UNC-formatted paths yet: " + path);
}
};
/**
* Utility function: Remove any leading/trailing backslashes
* from a string.
*/
let trimBackslashes = function trimBackslashes(string) {
return string.replace(/^\\+|\\+$/g,'');
};
/**
* Handling native paths.
*
* This module contains a number of functions destined to simplify
* working with native paths through a cross-platform API. Functions
* of this module will only work with the following assumptions:
*
* - paths are valid;
* - paths are defined with one of the grammars that this module can
* parse (see later);
* - all path concatenations go through function |join|.
*
* Limitations of this implementation.
*
* Windows supports 6 distinct grammars for paths. For the moment, this
* implementation supports the following subset:
*
* - drivename:backslash-separated components
* - backslash-separated components
*
* Additionally, |normalize| can convert a path containing slash-
* separated components to a path containing backslash-separated
* components.
*/
exports.OS.Win.Path = {
/**
* Return the final part of the path.
* The final part of the path is everything after the last "\\".
*/
basename: function basename(path) {
ensureNotUNC(path);
return path.slice(Math.max(path.lastIndexOf("\\"),
path.lastIndexOf(":")) + 1);
},
/**
* Return the directory part of the path. The directory part
* of the path is everything before the last "\\", including
* the drive/server name.
*
* If the path contains no directory, return the drive letter,
* or "." if the path contains no drive letter or if option
* |winNoDrive| is set.
*
* @param {string} path The path.
* @param {*=} options Platform-specific options controlling the behavior
* of this function. This implementation supports the following options:
* - |winNoDrive| If |true|, also remove the letter from the path name.
*/
dirname: function dirname(path, options) {
ensureNotUNC(path);
let noDrive = (options && options.winNoDrive);
// Find the last occurrence of "\\"
let index = path.lastIndexOf("\\");
if (index == -1) {
// If there is no directory component...
if (!noDrive) {
// Return the drive path if possible, falling back to "."
return this.winGetDrive(path) || ".";
} else {
// Or just "."
return ".";
}
}
// Ignore any occurrence of "\\: immediately before that one
while (index >= 0 && path[index] == "\\") {
--index;
}
// Compute what is left, removing the drive name if necessary
let start;
if (noDrive) {
start = (this.winGetDrive(path) || "").length;
} else {
start = 0;
}
return path.slice(start, index + 1);
},
/**
* Join path components.
* This is the recommended manner of getting the path of a file/subdirectory
* in a directory.
*
* Example: Obtaining $TMP/foo/bar in an OS-independent manner
* var tmpDir = OS.Path.to("TmpD");
* var path = OS.Path.join(tmpDir, "foo", "bar");
*
* Under Windows, this will return "$TMP\foo\bar".
*/
join: function join(path /*...*/) {
let paths = [];
let root;
let absolute = false;
for each(let subpath in arguments) {
let drive = this.winGetDrive(subpath);
let abs = this.winIsAbsolute(subpath);
if (drive) {
root = drive;
paths = [trimBackslashes(subpath.slice(drive.length))];
absolute = abs;
} else if (abs) {
paths = [trimBackslashes(subpath)];
absolute = true;
} else {
paths.push(trimBackslashes(subpath));
}
}
let result = "";
if (root) {
result += root;
}
if (absolute) {
result += "\\";
}
result += paths.join("\\");
return result;
},
/**
* Return the drive letter of a path, or |null| if the
* path does not contain a drive letter.
*/
winGetDrive: function winGetDrive(path) {
ensureNotUNC(path);
let index = path.indexOf(":");
if (index <= 0) return null;
return path.slice(0, index + 1);
},
/**
* Return |true| if the path is absolute, |false| otherwise.
*
* We consider that a path is absolute if it starts with "\\"
* or "driveletter:\\".
*/
winIsAbsolute: function winIsAbsolute(path) {
ensureNotUNC(path);
return this._winIsAbsolute(path);
},
/**
* As |winIsAbsolute|, but does not check for UNC.
* Useful mostly as an internal utility, for normalization.
*/
_winIsAbsolute: function _winIsAbsolute(path) {
let index = path.indexOf(":");
return path.length > index + 1 && path[index + 1] == "\\";
},
/**
* Normalize a path by removing any unneeded ".", "..", "\\".
* Also convert any "/" to a "\\".
*/
normalize: function normalize(path) {
let stack = [];
// Remove the drive (we will put it back at the end)
let root = this.winGetDrive(path);
if (root) {
path = path.slice(root.length);
}
// Remember whether we need to restore a leading "\\".
let absolute = this._winIsAbsolute(path);
// Normalize "/" to "\\"
path = path.replace("/", "\\");
// And now, fill |stack| from the components,
// popping whenever there is a ".."
path.split("\\").forEach(function loop(v) {
switch (v) {
case "": case ".": // Ignore
break;
case "..":
if (stack.length == 0) {
if (absolute) {
throw new Error("Path is ill-formed: attempting to go past root");
} else {
stack.push("..");
}
} else {
stack.pop();
}
break;
default:
stack.push(v);
}
});
// Put everything back together
let result = stack.join("\\");
if (absolute) {
result = "\\" + result;
}
if (root) {
result = root + result;
}
return result;
},
/**
* Return the components of a path.
* You should generally apply this function to a normalized path.
*
* @return {{
* {bool} absolute |true| if the path is absolute, |false| otherwise
* {array} components the string components of the path
* {string?} winDrive the drive or server for this path
* }}
*
* Other implementations may add additional OS-specific informations.
*/
split: function split(path) {
return {
absolute: this.winIsAbsolute(path),
winDrive: this.winGetDrive(path),
components: path.split("\\")
};
}
};
// Export useful stuff for extensibility
exports.OS.Win.libc = libc;
exports.OS.Win.declareFFI = declareFFI;
};
exports.OS.Win.File._init = init;
})(this);
}