merge m-c to fx-team

This commit is contained in:
Tim Taubert 2012-07-05 15:26:49 +02:00
commit 647b0f1dc7
22 changed files with 835 additions and 30 deletions

View File

@ -1023,7 +1023,7 @@ pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
pref("devtools.errorconsole.enabled", false);
// Developer toolbar and GCLI preferences
pref("devtools.toolbar.enabled", false);
pref("devtools.toolbar.enabled", true);
pref("devtools.toolbar.visible", false);
pref("devtools.gcli.allowSet", false);
pref("devtools.commands.dir", "");

View File

@ -519,7 +519,6 @@
autocheck="false"
hidden="true"
label="&devToolbarMenu.label;"
accesskey="&devToolbarMenu.accesskey;"
key="key_devToolbar"
command="Tools:DevToolbar"/>
<menuitem id="webConsole"

View File

@ -225,13 +225,6 @@
#endif
<key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
<key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
<key id="key_devToolbar" key="&devToolbar.commandkey;" command="Tools:DevToolbar"
#ifdef XP_MACOSX
modifiers="accel,alt"
#else
modifiers="accel,shift"
#endif
/>
<key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();"
#ifdef XP_MACOSX
modifiers="accel,alt"

View File

@ -6868,6 +6868,10 @@ FocusManager.prototype._checkShow = function() {
* available inputs
*/
FocusManager.prototype._shouldShowTooltip = function() {
if (!this._hasFocus) {
return { visible: false, reason: '!hasFocus' };
}
if (eagerHelper.value === Eagerness.NEVER) {
return { visible: false, reason: 'eagerHelper !== NEVER' };
}
@ -6896,6 +6900,10 @@ FocusManager.prototype._shouldShowTooltip = function() {
* available inputs
*/
FocusManager.prototype._shouldShowOutput = function() {
if (!this._hasFocus) {
return { visible: false, reason: '!hasFocus' };
}
if (this._recentOutput) {
return { visible: true, reason: 'recentOutput' };
}

View File

@ -179,6 +179,9 @@ DeveloperToolbar.prototype._onload = function DT_onload()
scratchpad: null
});
this.display.focusManager.addMonitoredElement(this.outputPanel._frame);
this.display.focusManager.addMonitoredElement(this._element);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged, this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
@ -187,7 +190,6 @@ DeveloperToolbar.prototype._onload = function DT_onload()
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabClose", this, false);
this._chromeWindow.getBrowser().addEventListener("load", this, true);
this._chromeWindow.getBrowser().addEventListener("beforeunload", this, true);
this._chromeWindow.addEventListener("resize", this, false);
this._initErrorsCount(this._chromeWindow.getBrowser().selectedTab);
@ -307,11 +309,13 @@ DeveloperToolbar.prototype.destroy = function DT_destroy()
this._chromeWindow.getBrowser().tabContainer.removeEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().removeEventListener("load", this, true);
this._chromeWindow.getBrowser().removeEventListener("beforeunload", this, true);
this._chromeWindow.removeEventListener("resize", this, false);
let tabs = this._chromeWindow.getBrowser().tabs;
Array.prototype.forEach.call(tabs, this._stopErrorsCount, this);
this.display.focusManager.removeMonitoredElement(this.outputPanel._frame);
this.display.focusManager.removeMonitoredElement(this._element);
this.display.onVisibilityChange.remove(this.outputPanel._visibilityChanged, this.outputPanel);
this.display.onVisibilityChange.remove(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
this.display.onOutput.remove(this.outputPanel._outputChanged, this.outputPanel);
@ -368,9 +372,6 @@ DeveloperToolbar.prototype.handleEvent = function DT_handleEvent(aEvent)
}
}
}
else if (aEvent.type == "resize") {
this.outputPanel._resize();
}
else if (aEvent.type == "TabClose") {
this._stopErrorsCount(aEvent.target);
}
@ -812,7 +813,7 @@ TooltipPanel.prototype._resize = function TP_resize()
}
let offset = 10 + Math.floor(this._dimensions.start * AVE_CHAR_WIDTH);
this._frame.style.marginLeft = offset + "px";
this._panel.style.marginLeft = offset + "px";
/*
// Bug 744906: UX review - Not sure if we want this code to fatten connector

View File

@ -40,6 +40,6 @@ function runTest() {
}
function getLeftMargin() {
let style = DeveloperToolbar.tooltipPanel._frame.style.marginLeft;
let style = DeveloperToolbar.tooltipPanel._panel.style.marginLeft;
return parseInt(style.slice(0, -2), 10);
}

View File

@ -237,8 +237,6 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
<!ENTITY devToolbarMenu.label "Developer Toolbar">
<!ENTITY devToolbarMenu.accesskey "v">
<!ENTITY devToolbar.commandkey "v">
<!ENTITY webConsoleButton.label "Web Console">
<!ENTITY inspectorButton.label "Inspector">

View File

@ -2398,6 +2398,7 @@ html|*#gcli-output-frame {
#gcli-tooltip {
border-width: 0;
background-color: transparent;
-moz-appearance: none;
margin-bottom: -2px;
}

View File

@ -31,7 +31,7 @@
margin-top: -1px;
margin-left: 8px;
width: 20px;
height: 0;
height: 10px;
border-left: 1px solid hsl(210,11%,10%);
border-right: 1px solid hsl(210,11%,10%);
background-color: hsl(210,11%,16%);

View File

@ -3139,6 +3139,7 @@ html|*#gcli-output-frame {
#gcli-tooltip {
border-width: 0;
background-color: transparent;
-moz-appearance: none;
margin-bottom: -2px;
}

View File

@ -32,10 +32,11 @@
margin-top: -1px;
margin-left: 8px;
width: 20px;
height: 0;
height: 10px;
border-left: 1px solid hsl(210,11%,10%);
border-right: 1px solid hsl(210,11%,10%);
background-color: hsl(210,11%,16%);
background-image: url(background-noise-toolbar.png);
}
.gcli-tt-description,

View File

@ -3073,6 +3073,7 @@ html|*#gcli-output-frame {
#gcli-tooltip {
border-width: 0;
background-color: transparent;
-moz-appearance: none;
margin-bottom: -2px;
}

View File

@ -2584,6 +2584,37 @@ nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
return NS_OK;
}
static PLDHashOperator
CloneSessionStorages(nsCStringHashKey::KeyType aKey, nsIDOMStorage* aStorage,
void* aUserArg)
{
nsIDocShell *docShell = static_cast<nsIDocShell*>(aUserArg);
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
if (pistorage) {
nsCOMPtr<nsIDOMStorage> storage = pistorage->Clone();
docShell->AddSessionStorage(pistorage->Principal(), storage);
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsDocShell::CloneSessionStoragesTo(nsIDocShell* aDocShell)
{
aDocShell->ClearSessionStorages();
mStorages.EnumerateRead(CloneSessionStorages, aDocShell);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::ClearSessionStorages()
{
mStorages.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
{

View File

@ -39,7 +39,7 @@ interface nsIWebBrowserPrint;
interface nsIVariant;
interface nsIPrivacyTransitionObserver;
[scriptable, uuid(6f60ac96-fa2c-41a5-92b4-29aaadbd7a7b)]
[scriptable, builtinclass, uuid(89ea9f32-18ec-413b-9e2c-ce9a4c851b1c)]
interface nsIDocShell : nsISupports
{
/**
@ -434,6 +434,19 @@ interface nsIDocShell : nsISupports
*/
void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
/**
* Clones all session storage objects and attaches them to the given docshell.
* Useful when duplicating tabs and their states.
*
* @param docShell the docshell to clone the sessionstorage objects to
*/
void cloneSessionStoragesTo(in nsIDocShell docShell);
/**
* Removes all WebApps session storage objects attached to the docshell.
*/
void clearSessionStorages();
/**
* Gets the channel for the currently loaded document, if any.
* For a new document load, this will be the channel of the previous document

View File

@ -9,6 +9,7 @@
#if defined(XP_UNIX)
#include "unistd.h"
#include "dirent.h"
#endif // defined(XP_UNIX)
#if defined(XP_MACOSX)
@ -264,6 +265,19 @@ static dom::ConstantSpec gLibcProperties[] =
#endif // defined(EWOULDBLOCK)
INT_CONSTANT(EXDEV),
#if defined(DT_UNKNOWN)
// Constants for |readdir|
INT_CONSTANT(DT_UNKNOWN),
INT_CONSTANT(DT_FIFO),
INT_CONSTANT(DT_CHR),
INT_CONSTANT(DT_DIR),
INT_CONSTANT(DT_BLK),
INT_CONSTANT(DT_REG),
INT_CONSTANT(DT_LNK),
INT_CONSTANT(DT_SOCK),
INT_CONSTANT(DT_WHT),
#endif // defined(DT_UNKNOWN)
// Constants used to define data structures
//
// Many data structures have different fields/sizes/etc. on
@ -274,9 +288,40 @@ static dom::ConstantSpec gLibcProperties[] =
#if defined(XP_UNIX)
// The size of |mode_t|.
{"OSFILE_SIZEOF_MODE_T", INT_TO_JSVAL(sizeof (mode_t)) },
{ "OSFILE_SIZEOF_MODE_T", INT_TO_JSVAL(sizeof (mode_t)) },
// Defining |dirent|.
// Size
{ "OSFILE_SIZEOF_DIRENT", INT_TO_JSVAL(sizeof (dirent)) },
// Offset of field |d_name|.
{ "OSFILE_OFFSETOF_DIRENT_D_NAME", INT_TO_JSVAL(offsetof (struct dirent, d_name)) },
// An upper bound to the length of field |d_name| of struct |dirent|.
// (may not be exact, depending on padding).
{ "OSFILE_SIZEOF_DIRENT_D_NAME", INT_TO_JSVAL(sizeof (struct dirent) - offsetof (struct dirent, d_name)) },
#if defined(DT_UNKNOWN)
// Position of field |d_type| in |dirent|
// Not strictly posix, but seems defined on all platforms
// except mingw32.
{ "OSFILE_OFFSETOF_DIRENT_D_TYPE", INT_TO_JSVAL(offsetof (struct dirent, d_type)) },
#endif // defined(DT_UNKNOWN)
#endif // defined(XP_UNIX)
// System configuration
// Under MacOSX, to avoid using deprecated functions that do not
// match the constants we define in this object (including
// |sizeof|/|offsetof| stuff, but not only), for a number of
// functions, we need to adapt the name of the symbols we are using,
// whenever macro _DARWIN_FEATURE_64_BIT_INODE is set. We export
// this value to be able to do so from JavaScript.
#if defined(_DARWIN_FEATURE_64_BIT_INODE)
{ "_DARWIN_FEATURE_64_BIT_INODE", INT_TO_JSVAL(1) },
#endif // defind(_DARWIN_FEATURE_64_BIT_INODE)
PROP_END
};
@ -296,6 +341,9 @@ static dom::ConstantSpec gWinProperties[] =
INT_CONSTANT(FORMAT_MESSAGE_FROM_SYSTEM),
INT_CONSTANT(FORMAT_MESSAGE_IGNORE_INSERTS),
// The max length of paths
INT_CONSTANT(MAX_PATH),
// CreateFile desired access
INT_CONSTANT(GENERIC_ALL),
INT_CONSTANT(GENERIC_EXECUTE),
@ -319,6 +367,7 @@ static dom::ConstantSpec gWinProperties[] =
INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
INT_CONSTANT(FILE_ATTRIBUTE_NORMAL),
INT_CONSTANT(FILE_ATTRIBUTE_READONLY),
INT_CONSTANT(FILE_ATTRIBUTE_REPARSE_POINT),
INT_CONSTANT(FILE_ATTRIBUTE_TEMPORARY),
// CreateFile error constant
@ -336,14 +385,19 @@ static dom::ConstantSpec gWinProperties[] =
// SetFilePointer error constant
INT_CONSTANT(INVALID_SET_FILE_POINTER),
// File attributes
INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
// MoveFile flags
INT_CONSTANT(MOVEFILE_COPY_ALLOWED),
INT_CONSTANT(MOVEFILE_REPLACE_EXISTING),
// Errors
INT_CONSTANT(ERROR_ACCESS_DENIED),
INT_CONSTANT(ERROR_FILE_EXISTS),
INT_CONSTANT(ERROR_FILE_NOT_FOUND),
INT_CONSTANT(ERROR_ACCESS_DENIED),
INT_CONSTANT(ERROR_NO_MORE_FILES),
PROP_END
};

View File

@ -440,6 +440,147 @@
ctypes.ssize_t,
projector(ctypes.ssize_t, true));
/**
* Utility class, used to build a |struct| type
* from a set of field names, types and offsets.
*
* @param {string} name The name of the |struct| type.
* @param {number} size The total size of the |struct| type in bytes.
*/
function HollowStructure(name, size) {
if (!name) {
throw new TypeError("HollowStructure expects a name");
}
if (!size || size < 0) {
throw new TypeError("HollowStructure expects a (positive) size");
}
// A mapping from offsets in the struct to name/type pairs
// (or nothing if no field starts at that offset).
this.offset_to_field_info = [];
// The name of the struct
this.name = name;
// The size of the struct, in bytes
this.size = size;
// The number of paddings inserted so far.
// Used to give distinct names to padding fields.
this._paddings = 0;
}
HollowStructure.prototype = {
/**
* Add a field at a given offset.
*
* @param {number} offset The offset at which to insert the field.
* @param {string} name The name of the field.
* @param {CType|Type} type The type of the field.
*/
add_field_at: function add_field_at(offset, name, type) {
if (offset === null) {
throw new TypeError("add_field_at requires a non-null offset");
}
if (!name) {
throw new TypeError("add_field_at requires a non-null name");
}
if (!type) {
throw new TypeError("add_field_at requires a non-null type");
}
if (type instanceof Type) {
type = type.implementation;
}
if (this.offset_to_field_info[offset]) {
throw new Error("HollowStructure " + this.name +
" already has a field at offset " + offset);
}
if (offset + type.size > this.size) {
throw new Error("HollowStructure " + this.name +
" cannot place a value of type " + type +
" at offset " + offset +
" without exceeding its size of " + this.size);
}
let field = {name: name, type:type};
this.offset_to_field_info[offset] = field;
},
/**
* Create a pseudo-field that will only serve as padding.
*
* @param {number} size The number of bytes in the field.
* @return {Object} An association field-name => field-type,
* as expected by |ctypes.StructType|.
*/
_makePaddingField: function makePaddingField(size) {
let field = ({});
field["padding_" + this._paddings] =
ctypes.ArrayType(ctypes.uint8_t, size);
this._paddings++;
return field;
},
/**
* Convert this |HollowStructure| into a |Type|.
*/
getType: function getType() {
// Contents of the structure, in the format expected
// by ctypes.StructType.
let struct = [];
let i = 0;
while (i < this.size) {
let currentField = this.offset_to_field_info[i];
if (!currentField) {
// No field was specified at this offset, we need to
// introduce some padding.
// Firstly, determine how many bytes of padding
let padding_length = 1;
while (i + padding_length < this.size
&& !this.offset_to_field_info[i + padding_length]) {
++padding_length;
}
// Then add the padding
struct.push(this._makePaddingField(padding_length));
// And proceed
i += padding_length;
} else {
// We have a field at this offset.
// Firstly, ensure that we do not have two overlapping fields
for (let j = 1; j < currentField.type.size; ++j) {
let candidateField = this.offset_to_field_info[i + j];
if (candidateField) {
throw new Error("Fields " + currentField.name +
" and " + candidateField.name +
" overlap at position " + (i + j));
}
}
// Then add the field
let field = ({});
field[currentField.name] = currentField.type;
struct.push(field);
// And proceed
i += currentField.type.size;
}
}
let result = new Type(this.name, ctypes.StructType(this.name, struct));
if (result.implementation.size != this.size) {
throw new Error("Wrong size for type " + this.name +
": expected " + this.size +
", found " + result.implementation.size +
" (" + result.implementation.toSource() + ")");
}
return result;
}
};
exports.OS.Shared.HollowStructure = HollowStructure;
/**
* Declare a function through js-ctypes
*
@ -473,6 +614,10 @@
let argtypes = [];
for (let i = 3; i < arguments.length; ++i) {
let current = arguments[i];
if (!current) {
throw new TypeError("Missing type for argument " + ( i - 3 ) +
" of symbol " + symbol);
}
if (!current.implementation) {
throw new TypeError("Missing implementation for argument " + (i - 3)
+ " of symbol " + symbol

View File

@ -46,7 +46,7 @@
// Open libc
let libc;
let libc_candidates = [ "libSystem.dylib",
let libc_candidates = [ "libsystem.B.dylib",
"libc.so.6",
"libc.so" ];
for (let i = 0; i < libc_candidates.length; ++i) {
@ -150,6 +150,47 @@
Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T),
{name: {value: "mode_t"}});
Types.DIR =
new Type("DIR",
ctypes.StructType("DIR"));
Types.null_or_DIR_ptr =
new Type("null_or_DIR*",
Types.DIR.out_ptr.implementation,
function(dir, operation) {
if (dir == null || dir.isNull()) {
return null;
}
return ctypes.CDataFinalizer(dir, _close_dir);
});
// Structure |dirent|
// Building this type is rather complicated, as its layout varies between
// variants of Unix. For this reason, we rely on a number of constants
// (computed in C from the C data structures) that give us the layout.
// The structure we compute looks like
// { int8_t[...] before_d_type; // ignored content
// int8_t d_type ;
// int8_t[...] before_d_name; // ignored content
// char[...] d_name;
// };
{
let dirent = new OS.Shared.HollowStructure("dirent",
OS.Constants.libc.OSFILE_SIZEOF_DIRENT);
dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_TYPE,
"d_type", ctypes.uint8_t);
dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_NAME,
"d_name", ctypes.ArrayType(ctypes.char, OS.Constants.libc.OSFILE_SIZEOF_DIRENT_D_NAME));
// We now have built |dirent|.
Types.dirent = dirent.getType();
LOG("dirent is: " + Types.dirent.implementation.toSource());
}
Types.null_or_dirent_ptr =
new Type("null_of_dirent",
Types.dirent.out_ptr.implementation);
// Declare libc functions as functions of |OS.Unix.File|
// Finalizer-related functions
@ -163,6 +204,16 @@
return fd.dispose();
};
let _close_dir =
libc.declare("closedir", ctypes.default_abi,
/*return */ctypes.int,
/*dirp*/ Types.DIR.in_ptr.implementation);
UnixFile.closedir = function closedir(fd) {
// Detach the finalizer and call |_close_dir|.
return fd.dispose();
};
UnixFile.free =
libc.declare("free", ctypes.default_abi,
/*return*/ ctypes.void_t,
@ -284,6 +335,12 @@
/*offset*/ Types.off_t,
/*whence*/ Types.int);
UnixFile.mkdir =
declareFFI("mkdir", ctypes.default_abi,
/*return*/ Types.int,
/*path*/ Types.string,
/*mode*/ Types.int);
UnixFile.mkstemp =
declareFFI("mkstemp", ctypes.default_abi,
/*return*/ Types.null_or_string,
@ -296,6 +353,11 @@
/*oflags*/Types.int,
/*mode*/ Types.int);
UnixFile.opendir =
declareFFI("opendir", ctypes.default_abi,
/*return*/ Types.null_or_DIR_ptr,
/*path*/ Types.string);
UnixFile.pread =
declareFFI("pread", ctypes.default_abi,
/*return*/ Types.negativeone_or_ssize_t,
@ -319,12 +381,33 @@
/*buf*/ Types.char.out_ptr,
/*nbytes*/Types.size_t);
if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
// Special case for MacOS X 10.5+
// Symbol name "readdir" still exists but is used for a
// deprecated function that does not match the
// constants of |OS.Constants.libc|.
UnixFile.readdir =
declareFFI("readdir$INODE64", ctypes.default_abi,
/*return*/Types.null_or_dirent_ptr,
/*dir*/ Types.DIR.in_ptr); // For MacOS X
} else {
UnixFile.readdir =
declareFFI("readdir", ctypes.default_abi,
/*return*/Types.null_or_dirent_ptr,
/*dir*/ Types.DIR.in_ptr); // Other Unices
}
UnixFile.rename =
declareFFI("rename", ctypes.default_abi,
/*return*/ Types.negativeone_or_nothing,
/*old*/ Types.string,
/*new*/ Types.string);
UnixFile.rmdir =
declareFFI("rmdir", ctypes.default_abi,
/*return*/ Types.int,
/*path*/ Types.string);
UnixFile.splice =
declareFFI("splice", ctypes.default_abi,
/*return*/ Types.long,

View File

@ -590,6 +590,112 @@
} // End of definition of copy/move
/**
* Iterate on one directory.
*
* This iterator will not enter subdirectories.
*
* @param {string} path The directory upon which to iterate.
* @param {*=} options Ignored in this implementation.
*
* @throws {File.Error} If |path| does not represent a directory or
* if the directory cannot be iterated.
* @constructor
*/
File.DirectoryIterator = function DirectoryIterator(path, options) {
let dir = throw_on_null("DirectoryIterator", UnixFile.opendir(path));
this._dir = dir;
this._path = path;
};
File.DirectoryIterator.prototype = {
__iterator__: function __iterator__() {
return this;
},
/**
* Return the next entry in the directory, if any such entry is
* available.
*
* Skip special directories "." and "..".
*
* @return {File.Entry} The next entry in the directory.
* @throws {StopIteration} Once all files in the directory have been
* encountered.
*/
next: function next() {
if (!this._dir) {
throw StopIteration;
}
for (let entry = UnixFile.readdir(this._dir);
entry != null && !entry.isNull();
entry = UnixFile.readdir(this._dir)) {
let contents = entry.contents;
if (contents.d_type == OS.Constants.libc.DT_DIR) {
let name = contents.d_name.readString();
if (name == "." || name == "..") {
continue;
}
}
return new File.DirectoryIterator.Entry(contents, this._path);
}
this.close();
throw StopIteration;
},
/**
* Close the iterator and recover all resources.
* You should call this once you have finished iterating on a directory.
*/
close: function close() {
if (!this._dir) return;
UnixFile.closedir(this._dir);
this._dir = null;
}
};
/**
* An entry in a directory.
*/
File.DirectoryIterator.Entry = function Entry(unix_entry, parent) {
// Copy the relevant part of |unix_entry| to ensure that
// our data is not overwritten prematurely.
this._d_type = unix_entry.d_type;
this._name = unix_entry.d_name.readString();
this._parent = parent;
};
File.DirectoryIterator.Entry.prototype = {
/**
* |true| if the entry is a directory, |false| otherwise
*/
get isDir() {
return this._d_type == OS.Constants.libc.DT_DIR;
},
/**
* |true| if the entry is a symbolic link, |false| otherwise
*/
get isLink() {
return this._d_type == OS.Constants.libc.DT_LNK;
},
/**
* The name of the entry.
* @type {string}
*/
get name() {
return this._name;
},
/**
* The full path to the entry.
*/
get path() {
delete this.path;
let path = OS.Unix.Path.join(this._parent, this.name);
Object.defineProperty(this, "path", {value: path});
return path;
}
};
/**
* Get/set the current directory.
@ -601,11 +707,10 @@
);
},
get: function() {
let path = UnixFile.getwd(null);
if (path.isNull()) {
throw new File.Error("getwd");
}
return ctypes.CDataFinalizer(path, UnixFile.free);
let path = UnixFile.get_current_dir_name?UnixFile.get_current_dir_name():
UnixFile.getwd_auto(null);
throw_on_null("curDir",path);
return path.readString();
}
}
);
@ -638,6 +743,13 @@
return result;
}
function throw_on_null(operation, result) {
if (result == null || (result.isNull && result.isNull())) {
throw new File.Error(operation);
}
return result;
}
File.POS_START = exports.OS.Constants.libc.SEEK_SET;
File.POS_CURRENT = exports.OS.Constants.libc.SEEK_CUR;
File.POS_END = exports.OS.Constants.libc.SEEK_END;

View File

@ -94,6 +94,24 @@
}
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;
@ -122,6 +140,40 @@
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 =
@ -133,6 +185,15 @@
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 =
@ -158,6 +219,24 @@
/*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,
@ -195,6 +274,11 @@
/*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,

View File

@ -453,12 +453,230 @@
);
};
/**
* A global value used to receive data during a
* |FindFirstFile|/|FindNextFile|.
*/
let gFindData = new OS.Shared.Type.FindData.implementation();
let gFindDataPtr = gFindData.address();
/**
* A global value used to receive data during time conversions.
*/
let gSystemTime = new OS.Shared.Type.SystemTime.implementation();
let gSystemTimePtr = gSystemTime.address();
/**
* Utility function: convert a FILETIME to a JavaScript Date.
*/
let FILETIME_to_Date = function FILETIME_to_Date(fileTime) {
LOG("fileTimeToDate:", fileTime);
if (fileTime == null) {
throw new TypeError("Expecting a non-null filetime");
}
LOG("fileTimeToDate normalized:", fileTime);
throw_on_zero("FILETIME_to_Date", WinFile.FileTimeToSystemTime(fileTime.address(),
gSystemTimePtr));
return new Date(gSystemTime.wYear, gSystemTime.wMonth,
gSystemTime.wDay, gSystemTime.wHour,
gSystemTime.wMinute, gSystemTime.wSecond,
gSystemTime.wMilliSeconds);
};
/**
* Iterate on one directory.
*
* This iterator will not enter subdirectories.
*
* @param {string} path The directory upon which to iterate.
* @param {*=} options Ignored in this implementation.
*
* @throws {File.Error} If |path| does not represent a directory or
* if the directory cannot be iterated.
* @constructor
*/
File.DirectoryIterator = function DirectoryIterator(path, options) {
if (options && options.winPattern) {
this._pattern = path + "\\" + options.winPattern;
} else {
this._pattern = path + "\\*";
}
this._handle = null;
this._path = path;
this._started = false;
};
File.DirectoryIterator.prototype = {
__iterator__: function __iterator__() {
return this;
},
/**
* Fetch the next entry in the directory.
*
* @return null If we have reached the end of the directory.
*/
_next: function _next() {
// Iterator is not fully initialized yet. Finish
// initialization.
if (!this._started) {
this._started = true;
this._handle = WinFile.FindFirstFile(this._pattern, gFindDataPtr);
if (this._handle == null) {
let error = ctypes.winLastError;
if (error == Const.ERROR_FILE_NOT_FOUND) {
this.close();
return null;
} else {
throw new File.Error("iter (FindFirstFile)", error);
}
}
return gFindData;
}
// We have closed this iterator already.
if (!this._handle) {
return null;
}
if (WinFile.FindNextFile(this._handle, gFindDataPtr)) {
return gFindData;
} else {
let error = ctypes.winLastError;
this.close();
if (error == Const.ERROR_NO_MORE_FILES) {
return null;
} else {
throw new File.Error("iter (FindNextFile)", error);
}
}
},
/**
* Return the next entry in the directory, if any such entry is
* available.
*
* Skip special directories "." and "..".
*
* @return {File.Entry} The next entry in the directory.
* @throws {StopIteration} Once all files in the directory have been
* encountered.
*/
next: function next() {
// FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
// that "." and ".." are absolutely normal file names if _path starts
// with such prefix
for (let entry = this._next(); entry != null; entry = this._next()) {
let name = entry.cFileName.readString();
if (name == "." || name == "..") {
continue;
}
return new File.DirectoryIterator.Entry(entry, this._path);
}
throw StopIteration;
},
close: function close() {
if (!this._handle) {
return;
}
WinFile.FindClose(this._handle);
this._handle = null;
}
};
File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
// Copy the relevant part of |win_entry| to ensure that
// our data is not overwritten prematurely.
if (!win_entry.dwFileAttributes) {
throw new TypeError();
}
this._dwFileAttributes = win_entry.dwFileAttributes;
this._name = win_entry.cFileName.readString();
if (!this._name) {
throw new TypeError("Empty name");
}
this._ftCreationTime = win_entry.ftCreationTime;
if (!win_entry.ftCreationTime) {
throw new TypeError();
}
this._ftLastAccessTime = win_entry.ftLastAccessTime;
if (!win_entry.ftLastAccessTime) {
throw new TypeError();
}
this._ftLastWriteTime = win_entry.ftLastWriteTime;
if (!win_entry.ftLastWriteTime) {
throw new TypeError();
}
if (!parent) {
throw new TypeError("Empty parent");
}
this._parent = parent;
};
File.DirectoryIterator.Entry.prototype = {
/**
* |true| if the entry is a directory, |false| otherwise
*/
get isDir() {
return this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY;
},
/**
* |true| if the entry is a symbolic link, |false| otherwise
*/
get isLink() {
return this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT;
},
/**
* The name of the entry.
* @type {string}
*/
get name() {
return this._name;
},
/**
* The creation time of this file.
* @type {Date}
*/
get winCreationTime() {
let date = FILETIME_to_Date(this._ftCreationTime);
delete this.winCreationTime;
Object.defineProperty(this, "winCreationTime", {value: date});
return date;
},
/**
* The last modification time of this file.
* @type {Date}
*/
get winLastWriteTime() {
let date = FILETIME_to_Date(this._ftLastWriteTime);
delete this.winLastWriteTime;
Object.defineProperty(this, "winLastWriteTime", {value: date});
return date;
},
/**
* The last access time of this file.
* @type {Date}
*/
get winLastAccessTime() {
let date = FILETIME_to_Date(this._ftLastAccessTime);
delete this.winLastAccessTime;
Object.defineProperty(this, "winLastAccessTime", {value: date});
return date;
},
/**
* The full path to the entry.
* @type {string}
*/
get path() {
delete this.path;
let path = OS.Win.Path.join(this._parent, this.name);
Object.defineProperty(this, "path", {value: path});
return path;
}
};
/**
* Get/set the current directory.
*/
Object.defineProperty(File, "curDir", {
set: function(path) {
throw_on_zero("set curDir",
throw_on_zero("set curDir",
WinFile.SetCurrentDirectory(path));
},
get: function() {

View File

@ -20,6 +20,7 @@ self.onmessage = function(msg) {
test_copy_existing_file();
test_read_write_file();
test_move_file();
test_iter_dir();
} catch (x) {
log("Catching error: " + x);
log("Stack: " + x.stack);
@ -198,3 +199,63 @@ function test_move_file()
}
function test_iter_dir()
{
ok(true, "test_iter_dir: Starting");
// Create a file, to be sure that it exists
let tmp_file_name = "test_osfile_front.tmp";
let tmp_file = OS.File.open(tmp_file_name, {write: true, trunc:true});
tmp_file.close();
let parent = OS.File.curDir;
ok(true, "test_iter_dir: directory " + parent);
let iterator = new OS.File.DirectoryIterator(parent);
ok(true, "test_iter_dir: iterator created");
let encountered_tmp_file = false;
for (let entry in iterator) {
// Checking that |name| can be decoded properly
ok(true, "test_iter_dir: encountering entry " + entry.name);
if (entry.name == tmp_file_name) {
encountered_tmp_file = true;
isnot(entry.isDir, "test_iter_dir: The temporary file is not a directory");
isnot(entry.isLink, "test_iter_dir: The temporary file is not a link");
}
let file;
let success = true;
try {
file = OS.File.open(entry.path);
} catch (x) {
if (x.becauseNoSuchFile) {
success = false;
}
}
if (file) {
file.close();
}
ok(success, "test_iter_dir: Entry " + entry.path + " exists");
if (OS.Win) {
let year = new Date().getFullYear();
let creation = entry.winCreationTime;
ok(creation, "test_iter_dir: Windows creation date exists: " + creation);
ok(creation.getFullYear() >= year - 1 && creation.getFullYear() <= year, "test_iter_dir: consistent creation date");
let lastWrite = entry.winLastWriteTime;
ok(lastWrite, "test_iter_dir: Windows lastWrite date exists: " + lastWrite);
ok(lastWrite.getFullYear() >= year - 1 && lastWrite.getFullYear() <= year, "test_iter_dir: consistent lastWrite date");
let lastAccess = entry.winLastAccessTime;
ok(lastAccess, "test_iter_dir: Windows lastAccess date exists: " + lastAccess);
ok(lastAccess.getFullYear() >= year - 1 && lastAccess.getFullYear() <= year, "test_iter_dir: consistent lastAccess date");
}
}
ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file");
ok(true, "test_iter_dir: Cleaning up");
iterator.close();
ok(true, "test_iter_dir: Complete");
}

View File

@ -18,6 +18,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDirService",
var FileUtils = {
MODE_RDONLY : 0x01,
MODE_WRONLY : 0x02,
MODE_RDWR : 0x04,
MODE_CREATE : 0x08,
MODE_APPEND : 0x10,
MODE_TRUNCATE : 0x20,