gecko-dev/dom/system/OSFileConstants.cpp
Kris Maglione 9c37be53dc Bug 1370027: Part 1 - Cleanly handle a subprocess child being reaped by NSPR. r=aswan
The first time any other code in the parent process uses NSPR (usually via
nsIProcess) to spawn a new process, it spawns a thread to contuously wait for
any child process to exit. This thread winds up reaping our child processes
before we get the chance to wait for them, which leads us to continuously poll
for them to exit.

We don't have a good way to handle this, but checking the error status of
waitpid at least prevents us from failing catastrophically.

MozReview-Commit-ID: 75Z1yUHUmjy

--HG--
extra : rebase_source : db45f781190b6fc84873c32c611134326736a1ba
2017-06-06 16:00:53 -07:00

1120 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/DebugOnly.h"
#include "fcntl.h"
#include "errno.h"
#include "prsystem.h"
#if defined(XP_UNIX)
#include "unistd.h"
#include "dirent.h"
#include "poll.h"
#include "sys/stat.h"
#if defined(XP_LINUX)
#include <sys/vfs.h>
#define statvfs statfs
#define f_frsize f_bsize
#else
#include "sys/statvfs.h"
#endif // defined(XP_LINUX)
#if !defined(ANDROID)
#include "sys/wait.h"
#include <spawn.h>
#endif // !defined(ANDROID)
#endif // defined(XP_UNIX)
#if defined(XP_LINUX)
#include <linux/fadvise.h>
#endif // defined(XP_LINUX)
#if defined(XP_MACOSX)
#include "copyfile.h"
#endif // defined(XP_MACOSX)
#if defined(XP_WIN)
#include <windows.h>
#include <accctrl.h>
#define PATH_MAX MAX_PATH
#endif // defined(XP_WIN)
#include "jsapi.h"
#include "jsfriendapi.h"
#include "BindingUtils.h"
// Used to provide information on the OS
#include "nsThreadUtils.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIXULRuntime.h"
#include "nsIPropertyBag2.h"
#include "nsXPCOMCIDInternal.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsSystemInfo.h"
#include "nsAutoPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozJSComponentLoader.h"
#include "OSFileConstants.h"
#include "nsIOSFileConstantsService.h"
#include "nsZipArchive.h"
#if defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
#define __dd_fd dd_fd
#endif
/**
* This module defines the basic libc constants (error numbers, open modes,
* etc.) used by OS.File and possibly other OS-bound JavaScript libraries.
*/
namespace mozilla {
// Use an anonymous namespace to hide the symbols and avoid any collision
// with, for instance, |extern bool gInitialized;|
namespace {
/**
* |true| if this module has been initialized, |false| otherwise
*/
bool gInitialized = false;
struct Paths {
/**
* The name of the directory holding all the libraries (libxpcom, libnss, etc.)
*/
nsString libDir;
nsString tmpDir;
nsString profileDir;
nsString localProfileDir;
/**
* The user's home directory
*/
nsString homeDir;
/**
* The user's desktop directory, if there is one. Otherwise this is
* the same as homeDir.
*/
nsString desktopDir;
/**
* The user's 'application data' directory.
* Windows:
* HOME = Documents and Settings\$USER\Application Data
* UAppData = $HOME[\$vendor]\$name
*
* Unix:
* HOME = ~
* UAppData = $HOME/.[$vendor/]$name
*
* Mac:
* HOME = ~
* UAppData = $HOME/Library/Application Support/$name
*/
nsString userApplicationDataDir;
#if defined(XP_WIN)
/**
* The user's application data directory.
*/
nsString winAppDataDir;
/**
* The programs subdirectory in the user's start menu directory.
*/
nsString winStartMenuProgsDir;
#endif // defined(XP_WIN)
#if defined(XP_MACOSX)
/**
* The user's Library directory.
*/
nsString macUserLibDir;
/**
* The Application directory, that stores applications installed in the
* system.
*/
nsString macLocalApplicationsDir;
/**
* The user's trash directory.
*/
nsString macTrashDir;
#endif // defined(XP_MACOSX)
Paths()
{
libDir.SetIsVoid(true);
tmpDir.SetIsVoid(true);
profileDir.SetIsVoid(true);
localProfileDir.SetIsVoid(true);
homeDir.SetIsVoid(true);
desktopDir.SetIsVoid(true);
userApplicationDataDir.SetIsVoid(true);
#if defined(XP_WIN)
winAppDataDir.SetIsVoid(true);
winStartMenuProgsDir.SetIsVoid(true);
#endif // defined(XP_WIN)
#if defined(XP_MACOSX)
macUserLibDir.SetIsVoid(true);
macLocalApplicationsDir.SetIsVoid(true);
macTrashDir.SetIsVoid(true);
#endif // defined(XP_MACOSX)
}
};
/**
* System directories.
*/
Paths* gPaths = nullptr;
/**
* (Unix) the umask, which goes in OS.Constants.Sys but
* can only be looked up (via the system-info service)
* on the main thread.
*/
uint32_t gUserUmask = 0;
} // namespace
/**
* Return the path to one of the special directories.
*
* @param aKey The key to the special directory (e.g. "TmpD", "ProfD", ...)
* @param aOutPath The path to the special directory. In case of error,
* the string is set to void.
*/
nsresult GetPathToSpecialDir(const char *aKey, nsString& aOutPath)
{
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(aKey, getter_AddRefs(file));
if (NS_FAILED(rv) || !file) {
return rv;
}
return file->GetPath(aOutPath);
}
/**
* In some cases, OSFileConstants may be instantiated before the
* profile is setup. In such cases, |OS.Constants.Path.profileDir| and
* |OS.Constants.Path.localProfileDir| are undefined. However, we want
* to ensure that this does not break existing code, so that future
* workers spawned after the profile is setup have these constants.
*
* For this purpose, we register an observer to set |gPaths->profileDir|
* and |gPaths->localProfileDir| once the profile is setup.
*/
class DelayedPathSetter final: public nsIObserver
{
~DelayedPathSetter() {}
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
DelayedPathSetter() {}
};
NS_IMPL_ISUPPORTS(DelayedPathSetter, nsIObserver)
NS_IMETHODIMP
DelayedPathSetter::Observe(nsISupports*, const char * aTopic, const char16_t*)
{
if (gPaths == nullptr) {
// Initialization of gPaths has not taken place, something is wrong,
// don't make things worse.
return NS_OK;
}
nsresult rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_50_DIR, gPaths->profileDir);
if (NS_FAILED(rv)) {
return rv;
}
rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_LOCAL_50_DIR, gPaths->localProfileDir);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
/**
* Perform the part of initialization that can only be
* executed on the main thread.
*/
nsresult InitOSFileConstants()
{
MOZ_ASSERT(NS_IsMainThread());
if (gInitialized) {
return NS_OK;
}
gInitialized = true;
nsAutoPtr<Paths> paths(new Paths);
// Initialize paths->libDir
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_XPCOM_LIBRARY_FILE, getter_AddRefs(file));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIFile> libDir;
rv = file->GetParent(getter_AddRefs(libDir));
if (NS_FAILED(rv)) {
return rv;
}
rv = libDir->GetPath(paths->libDir);
if (NS_FAILED(rv)) {
return rv;
}
// Setup profileDir and localProfileDir immediately if possible (we
// assume that NS_APP_USER_PROFILE_50_DIR and
// NS_APP_USER_PROFILE_LOCAL_50_DIR are set simultaneously)
rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_50_DIR, paths->profileDir);
if (NS_SUCCEEDED(rv)) {
rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_LOCAL_50_DIR, paths->localProfileDir);
}
// Otherwise, delay setup of profileDir/localProfileDir until they
// become available.
if (NS_FAILED(rv)) {
nsCOMPtr<nsIObserverService> obsService = do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<DelayedPathSetter> pathSetter = new DelayedPathSetter();
rv = obsService->AddObserver(pathSetter, "profile-do-change", false);
if (NS_FAILED(rv)) {
return rv;
}
}
// For other directories, ignore errors (they may be undefined on
// some platforms or in non-Firefox embeddings of Gecko).
GetPathToSpecialDir(NS_OS_TEMP_DIR, paths->tmpDir);
GetPathToSpecialDir(NS_OS_HOME_DIR, paths->homeDir);
GetPathToSpecialDir(NS_OS_DESKTOP_DIR, paths->desktopDir);
GetPathToSpecialDir(XRE_USER_APP_DATA_DIR, paths->userApplicationDataDir);
#if defined(XP_WIN)
GetPathToSpecialDir(NS_WIN_APPDATA_DIR, paths->winAppDataDir);
GetPathToSpecialDir(NS_WIN_PROGRAMS_DIR, paths->winStartMenuProgsDir);
#endif // defined(XP_WIN)
#if defined(XP_MACOSX)
GetPathToSpecialDir(NS_MAC_USER_LIB_DIR, paths->macUserLibDir);
GetPathToSpecialDir(NS_OSX_LOCAL_APPLICATIONS_DIR, paths->macLocalApplicationsDir);
GetPathToSpecialDir(NS_MAC_TRASH_DIR, paths->macTrashDir);
#endif // defined(XP_MACOSX)
gPaths = paths.forget();
// Get the umask from the system-info service.
// The property will always be present, but it will be zero on
// non-Unix systems.
// nsSystemInfo::gUserUmask is initialized by NS_InitXPCOM2 so we don't need
// to initialize the service.
gUserUmask = nsSystemInfo::gUserUmask;
return NS_OK;
}
/**
* Perform the cleaning up that can only be executed on the main thread.
*/
void CleanupOSFileConstants()
{
MOZ_ASSERT(NS_IsMainThread());
if (!gInitialized) {
return;
}
gInitialized = false;
delete gPaths;
}
/**
* Define a simple read-only property holding an integer.
*
* @param name The name of the constant. Used both as the JS name for the
* constant and to access its value. Must be defined.
*
* Produces a |ConstantSpec|.
*/
#define INT_CONSTANT(name) \
{ #name, JS::Int32Value(name) }
/**
* Define a simple read-only property holding an unsigned integer.
*
* @param name The name of the constant. Used both as the JS name for the
* constant and to access its value. Must be defined.
*
* Produces a |ConstantSpec|.
*/
#define UINT_CONSTANT(name) \
{ #name, JS::NumberValue(name) }
/**
* End marker for ConstantSpec
*/
#define PROP_END { nullptr, JS::UndefinedValue() }
// Define missing constants for Android
#if !defined(S_IRGRP)
#define S_IXOTH 0001
#define S_IWOTH 0002
#define S_IROTH 0004
#define S_IRWXO 0007
#define S_IXGRP 0010
#define S_IWGRP 0020
#define S_IRGRP 0040
#define S_IRWXG 0070
#define S_IXUSR 0100
#define S_IWUSR 0200
#define S_IRUSR 0400
#define S_IRWXU 0700
#endif // !defined(S_IRGRP)
/**
* The properties defined in libc.
*
* If you extend this list of properties, please
* separate categories ("errors", "open", etc.),
* keep properties organized by alphabetical order
* and #ifdef-away properties that are not portable.
*/
static const dom::ConstantSpec gLibcProperties[] =
{
// Arguments for open
INT_CONSTANT(O_APPEND),
#if defined(O_CLOEXEC)
INT_CONSTANT(O_CLOEXEC),
#endif // defined(O_CLOEXEC)
INT_CONSTANT(O_CREAT),
#if defined(O_DIRECTORY)
INT_CONSTANT(O_DIRECTORY),
#endif // defined(O_DIRECTORY)
#if defined(O_EVTONLY)
INT_CONSTANT(O_EVTONLY),
#endif // defined(O_EVTONLY)
INT_CONSTANT(O_EXCL),
#if defined(O_EXLOCK)
INT_CONSTANT(O_EXLOCK),
#endif // defined(O_EXLOCK)
#if defined(O_LARGEFILE)
INT_CONSTANT(O_LARGEFILE),
#endif // defined(O_LARGEFILE)
#if defined(O_NOFOLLOW)
INT_CONSTANT(O_NOFOLLOW),
#endif // defined(O_NOFOLLOW)
#if defined(O_NONBLOCK)
INT_CONSTANT(O_NONBLOCK),
#endif // defined(O_NONBLOCK)
INT_CONSTANT(O_RDONLY),
INT_CONSTANT(O_RDWR),
#if defined(O_RSYNC)
INT_CONSTANT(O_RSYNC),
#endif // defined(O_RSYNC)
#if defined(O_SHLOCK)
INT_CONSTANT(O_SHLOCK),
#endif // defined(O_SHLOCK)
#if defined(O_SYMLINK)
INT_CONSTANT(O_SYMLINK),
#endif // defined(O_SYMLINK)
#if defined(O_SYNC)
INT_CONSTANT(O_SYNC),
#endif // defined(O_SYNC)
INT_CONSTANT(O_TRUNC),
INT_CONSTANT(O_WRONLY),
#if defined(FD_CLOEXEC)
INT_CONSTANT(FD_CLOEXEC),
#endif // defined(FD_CLOEXEC)
#if defined(AT_EACCESS)
INT_CONSTANT(AT_EACCESS),
#endif //defined(AT_EACCESS)
#if defined(AT_FDCWD)
INT_CONSTANT(AT_FDCWD),
#endif //defined(AT_FDCWD)
#if defined(AT_SYMLINK_NOFOLLOW)
INT_CONSTANT(AT_SYMLINK_NOFOLLOW),
#endif //defined(AT_SYMLINK_NOFOLLOW)
#if defined(POSIX_FADV_SEQUENTIAL)
INT_CONSTANT(POSIX_FADV_SEQUENTIAL),
#endif //defined(POSIX_FADV_SEQUENTIAL)
// access
#if defined(F_OK)
INT_CONSTANT(F_OK),
INT_CONSTANT(R_OK),
INT_CONSTANT(W_OK),
INT_CONSTANT(X_OK),
#endif // defined(F_OK)
// modes
INT_CONSTANT(S_IRGRP),
INT_CONSTANT(S_IROTH),
INT_CONSTANT(S_IRUSR),
INT_CONSTANT(S_IRWXG),
INT_CONSTANT(S_IRWXO),
INT_CONSTANT(S_IRWXU),
INT_CONSTANT(S_IWGRP),
INT_CONSTANT(S_IWOTH),
INT_CONSTANT(S_IWUSR),
INT_CONSTANT(S_IXOTH),
INT_CONSTANT(S_IXGRP),
INT_CONSTANT(S_IXUSR),
// seek
INT_CONSTANT(SEEK_CUR),
INT_CONSTANT(SEEK_END),
INT_CONSTANT(SEEK_SET),
#if defined(XP_UNIX)
// poll
INT_CONSTANT(POLLERR),
INT_CONSTANT(POLLHUP),
INT_CONSTANT(POLLIN),
INT_CONSTANT(POLLNVAL),
INT_CONSTANT(POLLOUT),
// wait
#if defined(WNOHANG)
INT_CONSTANT(WNOHANG),
#endif // defined(WNOHANG)
// fcntl command values
INT_CONSTANT(F_GETLK),
INT_CONSTANT(F_SETFD),
INT_CONSTANT(F_SETFL),
INT_CONSTANT(F_SETLK),
INT_CONSTANT(F_SETLKW),
// flock type values
INT_CONSTANT(F_RDLCK),
INT_CONSTANT(F_WRLCK),
INT_CONSTANT(F_UNLCK),
// splice
#if defined(SPLICE_F_MOVE)
INT_CONSTANT(SPLICE_F_MOVE),
#endif // defined(SPLICE_F_MOVE)
#if defined(SPLICE_F_NONBLOCK)
INT_CONSTANT(SPLICE_F_NONBLOCK),
#endif // defined(SPLICE_F_NONBLOCK)
#if defined(SPLICE_F_MORE)
INT_CONSTANT(SPLICE_F_MORE),
#endif // defined(SPLICE_F_MORE)
#if defined(SPLICE_F_GIFT)
INT_CONSTANT(SPLICE_F_GIFT),
#endif // defined(SPLICE_F_GIFT)
#endif // defined(XP_UNIX)
// copyfile
#if defined(COPYFILE_DATA)
INT_CONSTANT(COPYFILE_DATA),
INT_CONSTANT(COPYFILE_EXCL),
INT_CONSTANT(COPYFILE_XATTR),
INT_CONSTANT(COPYFILE_STAT),
INT_CONSTANT(COPYFILE_ACL),
INT_CONSTANT(COPYFILE_MOVE),
#endif // defined(COPYFILE_DATA)
// error values
INT_CONSTANT(EACCES),
INT_CONSTANT(EAGAIN),
INT_CONSTANT(EBADF),
INT_CONSTANT(EEXIST),
INT_CONSTANT(EFAULT),
INT_CONSTANT(EFBIG),
INT_CONSTANT(EINVAL),
INT_CONSTANT(EINTR),
INT_CONSTANT(EIO),
INT_CONSTANT(EISDIR),
#if defined(ELOOP) // not defined with VC9
INT_CONSTANT(ELOOP),
#endif // defined(ELOOP)
INT_CONSTANT(EMFILE),
INT_CONSTANT(ENAMETOOLONG),
INT_CONSTANT(ENFILE),
INT_CONSTANT(ENOENT),
INT_CONSTANT(ENOMEM),
INT_CONSTANT(ENOSPC),
INT_CONSTANT(ENOTDIR),
INT_CONSTANT(ENXIO),
#if defined(EOPNOTSUPP) // not defined with VC 9
INT_CONSTANT(EOPNOTSUPP),
#endif // defined(EOPNOTSUPP)
#if defined(EOVERFLOW) // not defined with VC 9
INT_CONSTANT(EOVERFLOW),
#endif // defined(EOVERFLOW)
INT_CONSTANT(EPERM),
INT_CONSTANT(ERANGE),
#if defined(ETIMEDOUT) // not defined with VC 9
INT_CONSTANT(ETIMEDOUT),
#endif // defined(ETIMEDOUT)
#if defined(EWOULDBLOCK) // not defined with VC 9
INT_CONSTANT(EWOULDBLOCK),
#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),
#endif // defined(DT_UNKNOWN)
#if defined(S_IFIFO)
// Constants for |stat|
INT_CONSTANT(S_IFMT),
INT_CONSTANT(S_IFIFO),
INT_CONSTANT(S_IFCHR),
INT_CONSTANT(S_IFDIR),
INT_CONSTANT(S_IFBLK),
INT_CONSTANT(S_IFREG),
INT_CONSTANT(S_IFLNK),
INT_CONSTANT(S_IFSOCK),
#endif // defined(S_IFIFO)
INT_CONSTANT(PATH_MAX),
// Constants used to define data structures
//
// Many data structures have different fields/sizes/etc. on
// various OSes / versions of the same OS / platforms. For these
// data structures, we need to compute and export from C the size
// and, if necessary, the offset of fields, so as to be able to
// define the structure in JS.
#if defined(XP_UNIX)
// The size of |mode_t|.
{ "OSFILE_SIZEOF_MODE_T", JS::Int32Value(sizeof (mode_t)) },
// The size of |gid_t|.
{ "OSFILE_SIZEOF_GID_T", JS::Int32Value(sizeof (gid_t)) },
// The size of |uid_t|.
{ "OSFILE_SIZEOF_UID_T", JS::Int32Value(sizeof (uid_t)) },
// The size of |time_t|.
{ "OSFILE_SIZEOF_TIME_T", JS::Int32Value(sizeof (time_t)) },
// The size of |fsblkcnt_t|.
{ "OSFILE_SIZEOF_FSBLKCNT_T", JS::Int32Value(sizeof (fsblkcnt_t)) },
#if !defined(ANDROID)
// The size of |posix_spawn_file_actions_t|.
{ "OSFILE_SIZEOF_POSIX_SPAWN_FILE_ACTIONS_T", JS::Int32Value(sizeof (posix_spawn_file_actions_t)) },
#endif // !defined(ANDROID)
// Defining |dirent|.
// Size
{ "OSFILE_SIZEOF_DIRENT", JS::Int32Value(sizeof (dirent)) },
// Defining |flock|.
#if defined(XP_UNIX)
{ "OSFILE_SIZEOF_FLOCK", JS::Int32Value(sizeof (struct flock)) },
{ "OSFILE_OFFSETOF_FLOCK_L_START", JS::Int32Value(offsetof (struct flock, l_start)) },
{ "OSFILE_OFFSETOF_FLOCK_L_LEN", JS::Int32Value(offsetof (struct flock, l_len)) },
{ "OSFILE_OFFSETOF_FLOCK_L_PID", JS::Int32Value(offsetof (struct flock, l_pid)) },
{ "OSFILE_OFFSETOF_FLOCK_L_TYPE", JS::Int32Value(offsetof (struct flock, l_type)) },
{ "OSFILE_OFFSETOF_FLOCK_L_WHENCE", JS::Int32Value(offsetof (struct flock, l_whence)) },
#endif // defined(XP_UNIX)
// Offset of field |d_name|.
{ "OSFILE_OFFSETOF_DIRENT_D_NAME", JS::Int32Value(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", JS::Int32Value(sizeof (struct dirent) - offsetof (struct dirent, d_name)) },
// Defining |timeval|.
{ "OSFILE_SIZEOF_TIMEVAL", JS::Int32Value(sizeof (struct timeval)) },
{ "OSFILE_OFFSETOF_TIMEVAL_TV_SEC", JS::Int32Value(offsetof (struct timeval, tv_sec)) },
{ "OSFILE_OFFSETOF_TIMEVAL_TV_USEC", JS::Int32Value(offsetof (struct timeval, tv_usec)) },
#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", JS::Int32Value(offsetof (struct dirent, d_type)) },
#endif // defined(DT_UNKNOWN)
// Under MacOS X and BSDs, |dirfd| is a macro rather than a
// function, so we need a little help to get it to work
#if defined(dirfd)
{ "OSFILE_SIZEOF_DIR", JS::Int32Value(sizeof (DIR)) },
{ "OSFILE_OFFSETOF_DIR_DD_FD", JS::Int32Value(offsetof (DIR, __dd_fd)) },
#endif
// Defining |stat|
{ "OSFILE_SIZEOF_STAT", JS::Int32Value(sizeof (struct stat)) },
{ "OSFILE_OFFSETOF_STAT_ST_MODE", JS::Int32Value(offsetof (struct stat, st_mode)) },
{ "OSFILE_OFFSETOF_STAT_ST_UID", JS::Int32Value(offsetof (struct stat, st_uid)) },
{ "OSFILE_OFFSETOF_STAT_ST_GID", JS::Int32Value(offsetof (struct stat, st_gid)) },
{ "OSFILE_OFFSETOF_STAT_ST_SIZE", JS::Int32Value(offsetof (struct stat, st_size)) },
#if defined(HAVE_ST_ATIMESPEC)
{ "OSFILE_OFFSETOF_STAT_ST_ATIME", JS::Int32Value(offsetof (struct stat, st_atimespec)) },
{ "OSFILE_OFFSETOF_STAT_ST_MTIME", JS::Int32Value(offsetof (struct stat, st_mtimespec)) },
{ "OSFILE_OFFSETOF_STAT_ST_CTIME", JS::Int32Value(offsetof (struct stat, st_ctimespec)) },
#else
{ "OSFILE_OFFSETOF_STAT_ST_ATIME", JS::Int32Value(offsetof (struct stat, st_atime)) },
{ "OSFILE_OFFSETOF_STAT_ST_MTIME", JS::Int32Value(offsetof (struct stat, st_mtime)) },
{ "OSFILE_OFFSETOF_STAT_ST_CTIME", JS::Int32Value(offsetof (struct stat, st_ctime)) },
#endif // defined(HAVE_ST_ATIME)
// Several OSes have a birthtime field. For the moment, supporting only Darwin.
#if defined(_DARWIN_FEATURE_64_BIT_INODE)
{ "OSFILE_OFFSETOF_STAT_ST_BIRTHTIME", JS::Int32Value(offsetof (struct stat, st_birthtime)) },
#endif // defined(_DARWIN_FEATURE_64_BIT_INODE)
// Defining |statvfs|
{ "OSFILE_SIZEOF_STATVFS", JS::Int32Value(sizeof (struct statvfs)) },
{ "OSFILE_OFFSETOF_STATVFS_F_FRSIZE", JS::Int32Value(offsetof (struct statvfs, f_frsize)) },
{ "OSFILE_OFFSETOF_STATVFS_F_BAVAIL", JS::Int32Value(offsetof (struct statvfs, f_bavail)) },
#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", JS::Int32Value(1) },
#endif // defined(_DARWIN_FEATURE_64_BIT_INODE)
// Similar feature for Linux
#if defined(_STAT_VER)
INT_CONSTANT(_STAT_VER),
#endif // defined(_STAT_VER)
PROP_END
};
#if defined(XP_WIN)
/**
* The properties defined in windows.h.
*
* If you extend this list of properties, please
* separate categories ("errors", "open", etc.),
* keep properties organized by alphabetical order
* and #ifdef-away properties that are not portable.
*/
static const dom::ConstantSpec gWinProperties[] =
{
// FormatMessage flags
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),
INT_CONSTANT(GENERIC_READ),
INT_CONSTANT(GENERIC_WRITE),
// CreateFile share mode
INT_CONSTANT(FILE_SHARE_DELETE),
INT_CONSTANT(FILE_SHARE_READ),
INT_CONSTANT(FILE_SHARE_WRITE),
// CreateFile creation disposition
INT_CONSTANT(CREATE_ALWAYS),
INT_CONSTANT(CREATE_NEW),
INT_CONSTANT(OPEN_ALWAYS),
INT_CONSTANT(OPEN_EXISTING),
INT_CONSTANT(TRUNCATE_EXISTING),
// CreateFile attributes
INT_CONSTANT(FILE_ATTRIBUTE_ARCHIVE),
INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
INT_CONSTANT(FILE_ATTRIBUTE_HIDDEN),
INT_CONSTANT(FILE_ATTRIBUTE_NORMAL),
INT_CONSTANT(FILE_ATTRIBUTE_READONLY),
INT_CONSTANT(FILE_ATTRIBUTE_REPARSE_POINT),
INT_CONSTANT(FILE_ATTRIBUTE_SYSTEM),
INT_CONSTANT(FILE_ATTRIBUTE_TEMPORARY),
INT_CONSTANT(FILE_FLAG_BACKUP_SEMANTICS),
// CreateFile error constant
{ "INVALID_HANDLE_VALUE", JS::Int32Value(INT_PTR(INVALID_HANDLE_VALUE)) },
// CreateFile flags
INT_CONSTANT(FILE_FLAG_DELETE_ON_CLOSE),
// SetFilePointer methods
INT_CONSTANT(FILE_BEGIN),
INT_CONSTANT(FILE_CURRENT),
INT_CONSTANT(FILE_END),
// SetFilePointer error constant
UINT_CONSTANT(INVALID_SET_FILE_POINTER),
// File attributes
INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
// MoveFile flags
INT_CONSTANT(MOVEFILE_COPY_ALLOWED),
INT_CONSTANT(MOVEFILE_REPLACE_EXISTING),
// GetFileAttributes error constant
INT_CONSTANT(INVALID_FILE_ATTRIBUTES),
// GetNamedSecurityInfo and SetNamedSecurityInfo constants
INT_CONSTANT(UNPROTECTED_DACL_SECURITY_INFORMATION),
INT_CONSTANT(SE_FILE_OBJECT),
INT_CONSTANT(DACL_SECURITY_INFORMATION),
// Errors
INT_CONSTANT(ERROR_INVALID_HANDLE),
INT_CONSTANT(ERROR_ACCESS_DENIED),
INT_CONSTANT(ERROR_DIR_NOT_EMPTY),
INT_CONSTANT(ERROR_FILE_EXISTS),
INT_CONSTANT(ERROR_ALREADY_EXISTS),
INT_CONSTANT(ERROR_FILE_NOT_FOUND),
INT_CONSTANT(ERROR_NO_MORE_FILES),
INT_CONSTANT(ERROR_PATH_NOT_FOUND),
INT_CONSTANT(ERROR_BAD_ARGUMENTS),
INT_CONSTANT(ERROR_SHARING_VIOLATION),
INT_CONSTANT(ERROR_NOT_SUPPORTED),
PROP_END
};
#endif // defined(XP_WIN)
/**
* Get a field of an object as an object.
*
* If the field does not exist, create it. If it exists but is not an
* object, throw a JS error.
*/
JSObject *GetOrCreateObjectProperty(JSContext *cx, JS::Handle<JSObject*> aObject,
const char *aProperty)
{
JS::Rooted<JS::Value> val(cx);
if (!JS_GetProperty(cx, aObject, aProperty, &val)) {
return nullptr;
}
if (!val.isUndefined()) {
if (val.isObject()) {
return &val.toObject();
}
JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
JSMSG_UNEXPECTED_TYPE,
aProperty, "not an object");
return nullptr;
}
return JS_DefineObject(cx, aObject, aProperty, nullptr, JSPROP_ENUMERATE);
}
/**
* Set a property of an object from a nsString.
*
* If the nsString is void (i.e. IsVoid is true), do nothing.
*/
bool SetStringProperty(JSContext *cx, JS::Handle<JSObject*> aObject, const char *aProperty,
const nsString aValue)
{
if (aValue.IsVoid()) {
return true;
}
JSString* strValue = JS_NewUCStringCopyZ(cx, aValue.get());
NS_ENSURE_TRUE(strValue, false);
JS::Rooted<JS::Value> valValue(cx, JS::StringValue(strValue));
return JS_SetProperty(cx, aObject, aProperty, valValue);
}
/**
* Define OS-specific constants.
*
* This function creates or uses JS object |OS.Constants| to store
* all its constants.
*/
bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
{
MOZ_ASSERT(gInitialized);
if (gPaths == nullptr) {
// If an initialization error was ignored, we may end up with
// |gInitialized == true| but |gPaths == nullptr|. We cannot
// |MOZ_ASSERT| this, as this would kill precompile_cache.js,
// so we simply return an error.
JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
JSMSG_CANT_OPEN,
"OSFileConstants", "initialization has failed");
return false;
}
JS::Rooted<JSObject*> objOS(cx);
if (!(objOS = GetOrCreateObjectProperty(cx, global, "OS"))) {
return false;
}
JS::Rooted<JSObject*> objConstants(cx);
if (!(objConstants = GetOrCreateObjectProperty(cx, objOS, "Constants"))) {
return false;
}
// Build OS.Constants.libc
JS::Rooted<JSObject*> objLibc(cx);
if (!(objLibc = GetOrCreateObjectProperty(cx, objConstants, "libc"))) {
return false;
}
if (!dom::DefineConstants(cx, objLibc, gLibcProperties)) {
return false;
}
#if defined(XP_WIN)
// Build OS.Constants.Win
JS::Rooted<JSObject*> objWin(cx);
if (!(objWin = GetOrCreateObjectProperty(cx, objConstants, "Win"))) {
return false;
}
if (!dom::DefineConstants(cx, objWin, gWinProperties)) {
return false;
}
#endif // defined(XP_WIN)
// Build OS.Constants.Sys
JS::Rooted<JSObject*> objSys(cx);
if (!(objSys = GetOrCreateObjectProperty(cx, objConstants, "Sys"))) {
return false;
}
#if defined(MOZ_WIDGET_GONK)
JSString* strVersion = JS_NewStringCopyZ(cx, "Gonk");
if (!strVersion){
return false;
}
JS::Rooted<JS::Value> valVersion(cx, JS::StringValue(strVersion));
if (!JS_SetProperty(cx, objSys, "Name", valVersion)) {
return false;
}
#else
nsCOMPtr<nsIXULRuntime> runtime = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
if (runtime) {
nsAutoCString os;
DebugOnly<nsresult> rv = runtime->GetOS(os);
MOZ_ASSERT(NS_SUCCEEDED(rv));
JSString* strVersion = JS_NewStringCopyZ(cx, os.get());
if (!strVersion) {
return false;
}
JS::Rooted<JS::Value> valVersion(cx, JS::StringValue(strVersion));
if (!JS_SetProperty(cx, objSys, "Name", valVersion)) {
return false;
}
}
#endif // defined(MOZ_WIDGET_GONK)
#if defined(DEBUG)
JS::Rooted<JS::Value> valDebug(cx, JS::TrueValue());
if (!JS_SetProperty(cx, objSys, "DEBUG", valDebug)) {
return false;
}
#endif
#if defined(HAVE_64BIT_BUILD)
JS::Rooted<JS::Value> valBits(cx, JS::Int32Value(64));
#else
JS::Rooted<JS::Value> valBits(cx, JS::Int32Value(32));
#endif //defined (HAVE_64BIT_BUILD)
if (!JS_SetProperty(cx, objSys, "bits", valBits)) {
return false;
}
if (!JS_DefineProperty(cx, objSys, "umask", gUserUmask,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
return false;
}
// Build OS.Constants.Path
JS::Rooted<JSObject*> objPath(cx);
if (!(objPath = GetOrCreateObjectProperty(cx, objConstants, "Path"))) {
return false;
}
// Locate libxul
// Note that we don't actually provide the full path, only the name of the
// library, which is sufficient to link to the library using js-ctypes.
#if defined(XP_MACOSX)
// Under MacOS X, for some reason, libxul is called simply "XUL",
// and we need to provide the full path.
nsAutoString libxul;
libxul.Append(gPaths->libDir);
libxul.AppendLiteral("/XUL");
#else
// On other platforms, libxul is a library "xul" with regular
// library prefix/suffix.
nsAutoString libxul;
libxul.AppendLiteral(DLL_PREFIX);
libxul.AppendLiteral("xul");
libxul.AppendLiteral(DLL_SUFFIX);
#endif // defined(XP_MACOSX)
if (!SetStringProperty(cx, objPath, "libxul", libxul)) {
return false;
}
if (!SetStringProperty(cx, objPath, "libDir", gPaths->libDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "tmpDir", gPaths->tmpDir)) {
return false;
}
// Configure profileDir only if it is available at this stage
if (!gPaths->profileDir.IsVoid()
&& !SetStringProperty(cx, objPath, "profileDir", gPaths->profileDir)) {
return false;
}
// Configure localProfileDir only if it is available at this stage
if (!gPaths->localProfileDir.IsVoid()
&& !SetStringProperty(cx, objPath, "localProfileDir", gPaths->localProfileDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "homeDir", gPaths->homeDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "desktopDir", gPaths->desktopDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "userApplicationDataDir", gPaths->userApplicationDataDir)) {
return false;
}
#if defined(XP_WIN)
if (!SetStringProperty(cx, objPath, "winAppDataDir", gPaths->winAppDataDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "winStartMenuProgsDir", gPaths->winStartMenuProgsDir)) {
return false;
}
#endif // defined(XP_WIN)
#if defined(XP_MACOSX)
if (!SetStringProperty(cx, objPath, "macUserLibDir", gPaths->macUserLibDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "macLocalApplicationsDir", gPaths->macLocalApplicationsDir)) {
return false;
}
if (!SetStringProperty(cx, objPath, "macTrashDir", gPaths->macTrashDir)) {
return false;
}
#endif // defined(XP_MACOSX)
// sqlite3 is linked from different places depending on the platform
nsAutoString libsqlite3;
#if defined(ANDROID)
// On Android, we use the system's libsqlite3
libsqlite3.AppendLiteral(DLL_PREFIX);
libsqlite3.AppendLiteral("sqlite3");
libsqlite3.AppendLiteral(DLL_SUFFIX);
#elif defined(XP_WIN)
// On Windows, for some reason, this is part of nss3.dll
libsqlite3.AppendLiteral(DLL_PREFIX);
libsqlite3.AppendLiteral("nss3");
libsqlite3.AppendLiteral(DLL_SUFFIX);
#else
// On other platforms, we link sqlite3 into libxul
libsqlite3 = libxul;
#endif // defined(ANDROID) || defined(XP_WIN)
if (!SetStringProperty(cx, objPath, "libsqlite3", libsqlite3)) {
return false;
}
return true;
}
NS_IMPL_ISUPPORTS(OSFileConstantsService, nsIOSFileConstantsService)
OSFileConstantsService::OSFileConstantsService()
{
MOZ_ASSERT(NS_IsMainThread());
}
OSFileConstantsService::~OSFileConstantsService()
{
mozilla::CleanupOSFileConstants();
}
NS_IMETHODIMP
OSFileConstantsService::Init(JSContext *aCx)
{
nsresult rv = mozilla::InitOSFileConstants();
if (NS_FAILED(rv)) {
return rv;
}
mozJSComponentLoader* loader = mozJSComponentLoader::Get();
JS::Rooted<JSObject*> targetObj(aCx);
loader->FindTargetObject(aCx, &targetObj);
if (!mozilla::DefineOSFileConstants(aCx, targetObj)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
} // namespace mozilla