dom/system/OSFileConstants.cpp
author Timothy Guan-tin Chien <timdream@gmail.com>
Mon, 07 Jan 2019 16:00:55 +0000
changeset 506594 2046aa36055220d9187f968fca3ab070d2de7665
parent 505383 6f3709b3878117466168c40affa7bca0b60cf75b
child 511623 5f4630838d46dd81dadb13220a4af0da9e23a619
permissions -rw-r--r--
Bug 1516292 - Use isSameNode() to compare mozFullScreenElement and the video host element. r=edgar, a=RyanVM This does what's done in bug 1505547 but in a different way. For some reason, access shadowRoot.host in the function can trigger an assertion. this.video is a Proxy so it cannot be compared, but all its methods are proxied. Differential Revision: https://phabricator.services.mozilla.com/D15767

/* -*- 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>

#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif

#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 "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozJSComponentLoader.h"

#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.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 {

namespace {

StaticRefPtr<OSFileConstantsService> gInstance;

}  // anonymous namespace

struct OSFileConstantsService::Paths {
  /**
   * The name of the directory holding all the libraries (libxpcom, libnss,
   * etc.)
   */
  nsString libDir;
  nsString tmpDir;
  nsString profileDir;
  nsString localProfileDir;

  Paths() {
    libDir.SetIsVoid(true);
    tmpDir.SetIsVoid(true);
    profileDir.SetIsVoid(true);
    localProfileDir.SetIsVoid(true);
  }
};

/**
 * 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 |mPaths->profileDir|
 * and |mPaths->localProfileDir| once the profile is setup.
 */
NS_IMETHODIMP
OSFileConstantsService::Observe(nsISupports *, const char *aTopic,
                                const char16_t *) {
  if (!mInitialized) {
    // Initialization has not taken place, something is wrong,
    // don't make things worse.
    return NS_OK;
  }

  nsresult rv =
      GetPathToSpecialDir(NS_APP_USER_PROFILE_50_DIR, mPaths->profileDir);
  if (NS_FAILED(rv)) {
    return rv;
  }
  rv = GetPathToSpecialDir(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                           mPaths->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 OSFileConstantsService::InitOSFileConstants() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mInitialized) {
    return NS_OK;
  }

  UniquePtr<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;
    }
    rv = obsService->AddObserver(this, "profile-do-change", false);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  GetPathToSpecialDir(NS_OS_TEMP_DIR, paths->tmpDir);

  mPaths = std::move(paths);

  // 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.
  mUserUmask = nsSystemInfo::gUserUmask;

  mInitialized = true;
  return NS_OK;
}

/**
 * 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 OSFileConstantsService::DefineOSFileConstants(
    JSContext *aCx, JS::Handle<JSObject *> aGlobal) {
  if (!mInitialized) {
    JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                              JSMSG_CANT_OPEN, "OSFileConstants",
                              "initialization has failed");
    return false;
  }

  JS::Rooted<JSObject *> objOS(aCx);
  if (!(objOS = GetOrCreateObjectProperty(aCx, aGlobal, "OS"))) {
    return false;
  }
  JS::Rooted<JSObject *> objConstants(aCx);
  if (!(objConstants = GetOrCreateObjectProperty(aCx, objOS, "Constants"))) {
    return false;
  }

  // Build OS.Constants.libc

  JS::Rooted<JSObject *> objLibc(aCx);
  if (!(objLibc = GetOrCreateObjectProperty(aCx, objConstants, "libc"))) {
    return false;
  }
  if (!dom::DefineConstants(aCx, objLibc, gLibcProperties)) {
    return false;
  }

#if defined(XP_WIN)
  // Build OS.Constants.Win

  JS::Rooted<JSObject *> objWin(aCx);
  if (!(objWin = GetOrCreateObjectProperty(aCx, objConstants, "Win"))) {
    return false;
  }
  if (!dom::DefineConstants(aCx, objWin, gWinProperties)) {
    return false;
  }
#endif  // defined(XP_WIN)

  // Build OS.Constants.Sys

  JS::Rooted<JSObject *> objSys(aCx);
  if (!(objSys = GetOrCreateObjectProperty(aCx, objConstants, "Sys"))) {
    return false;
  }

  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(aCx, os.get());
    if (!strVersion) {
      return false;
    }

    JS::Rooted<JS::Value> valVersion(aCx, JS::StringValue(strVersion));
    if (!JS_SetProperty(aCx, objSys, "Name", valVersion)) {
      return false;
    }
  }

#if defined(DEBUG)
  JS::Rooted<JS::Value> valDebug(aCx, JS::TrueValue());
  if (!JS_SetProperty(aCx, objSys, "DEBUG", valDebug)) {
    return false;
  }
#endif

#if defined(HAVE_64BIT_BUILD)
  JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(64));
#else
  JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(32));
#endif  // defined (HAVE_64BIT_BUILD)
  if (!JS_SetProperty(aCx, objSys, "bits", valBits)) {
    return false;
  }

  if (!JS_DefineProperty(
          aCx, objSys, "umask", mUserUmask,
          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
    return false;
  }

  // Build OS.Constants.Path

  JS::Rooted<JSObject *> objPath(aCx);
  if (!(objPath = GetOrCreateObjectProperty(aCx, 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(mPaths->libDir);
  libxul.AppendLiteral("/XUL");
#else
  // On other platforms, libxul is a library "xul" with regular
  // library prefix/suffix.
  nsAutoString libxul;
  libxul.AppendLiteral(MOZ_DLL_PREFIX);
  libxul.AppendLiteral("xul");
  libxul.AppendLiteral(MOZ_DLL_SUFFIX);
#endif  // defined(XP_MACOSX)

  if (!SetStringProperty(aCx, objPath, "libxul", libxul)) {
    return false;
  }

  if (!SetStringProperty(aCx, objPath, "libDir", mPaths->libDir)) {
    return false;
  }

  if (!SetStringProperty(aCx, objPath, "tmpDir", mPaths->tmpDir)) {
    return false;
  }

  // Configure profileDir only if it is available at this stage
  if (!mPaths->profileDir.IsVoid() &&
      !SetStringProperty(aCx, objPath, "profileDir", mPaths->profileDir)) {
    return false;
  }

  // Configure localProfileDir only if it is available at this stage
  if (!mPaths->localProfileDir.IsVoid() &&
      !SetStringProperty(aCx, objPath, "localProfileDir",
                         mPaths->localProfileDir)) {
    return false;
  }

  // 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(MOZ_DLL_PREFIX);
  libsqlite3.AppendLiteral("sqlite3");
  libsqlite3.AppendLiteral(MOZ_DLL_SUFFIX);
#elif defined(XP_WIN)
  // On Windows, for some reason, this is part of nss3.dll
  libsqlite3.AppendLiteral(MOZ_DLL_PREFIX);
  libsqlite3.AppendLiteral("nss3");
  libsqlite3.AppendLiteral(MOZ_DLL_SUFFIX);
#else
  // On other platforms, we link sqlite3 into libxul
  libsqlite3 = libxul;
#endif  // defined(ANDROID) || defined(XP_WIN)

  if (!SetStringProperty(aCx, objPath, "libsqlite3", libsqlite3)) {
    return false;
  }

  return true;
}

NS_IMPL_ISUPPORTS(OSFileConstantsService, nsIOSFileConstantsService,
                  nsIObserver)

/* static */ already_AddRefed<OSFileConstantsService>
OSFileConstantsService::GetOrCreate() {
  if (!gInstance) {
    MOZ_ASSERT(NS_IsMainThread());

    RefPtr<OSFileConstantsService> service = new OSFileConstantsService();
    nsresult rv = service->InitOSFileConstants();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }

    gInstance = service.forget();
    ClearOnShutdown(&gInstance);
  }

  RefPtr<OSFileConstantsService> copy = gInstance;
  return copy.forget();
}

OSFileConstantsService::OSFileConstantsService()
    : mInitialized(false), mUserUmask(0) {
  MOZ_ASSERT(NS_IsMainThread());
}

OSFileConstantsService::~OSFileConstantsService() {
  MOZ_ASSERT(NS_IsMainThread());
}

NS_IMETHODIMP
OSFileConstantsService::Init(JSContext *aCx) {
  MOZ_ASSERT(NS_IsMainThread());

  nsresult rv = InitOSFileConstants();
  if (NS_FAILED(rv)) {
    return rv;
  }

  mozJSComponentLoader *loader = mozJSComponentLoader::Get();
  JS::Rooted<JSObject *> targetObj(aCx);
  loader->FindTargetObject(aCx, &targetObj);

  if (!DefineOSFileConstants(aCx, targetObj)) {
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

}  // namespace mozilla