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