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

#ifndef mozilla_GeckoArgs_h
#define mozilla_GeckoArgs_h

#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/ipc/SharedMemoryHandle.h"

#include <charconv>
#include <climits>
#include <string>
#include <string_view>
#include <vector>

namespace mozilla {

namespace geckoargs {

// Type used for passing arguments to a content process, including OS files.
struct ChildProcessArgs {
  std::vector<std::string> mArgs;
  std::vector<UniqueFileHandle> mFiles;
#ifdef XP_DARWIN
  std::vector<UniqueMachSendRight> mSendRights;
  std::vector<UniqueMachReceiveRight> mReceiveRights;
#endif
};

#ifdef XP_UNIX
// On some unix platforms, file handles are passed down without using a fixed
// file descriptor. This method can be used to override the default mapping.
void SetPassedFileHandles(Span<int> aFiles);
void SetPassedFileHandles(std::vector<UniqueFileHandle>&& aFiles);

// Add the file handles from a ChildProcessArgs to a fdsToRemap table.
void AddToFdsToRemap(const ChildProcessArgs& aArgs,
                     std::vector<std::pair<int, int>>& aFdsToRemap);
#endif

#ifdef XP_DARWIN
// Size of the internal static array of mach send rights. This acts as a limit
// to the number of mach send rights which can be passed on the command line.
constexpr size_t kMaxPassedMachSendRights = 10;

// Size of the internal static array of mach receive rights. This acts as a
// limit to the number of mach receive rights which can be passed on the
// command line.
constexpr size_t kMaxPassedMachReceiveRights = 1;

// Fill the internal static array with the mach send rights which were passed
// from the parent process.
void SetPassedMachSendRights(std::vector<UniqueMachSendRight>&& aSendRights);
void SetPassedMachReceiveRights(
    std::vector<UniqueMachReceiveRight>&& aReceiveRights);
#endif

template <typename T>
struct CommandLineArg {
  bool IsPresent(int& aArgc, char** aArgv) const {
    return ARG_FOUND ==
           CheckArg(aArgc, aArgv, sMatch, nullptr, CheckArgFlag::None);
  }

  Maybe<T> Get(int& aArgc, char** aArgv,
               const CheckArgFlag aFlags = CheckArgFlag::RemoveArg) {
    return GetCommon(sMatch, aArgc, aArgv, aFlags);
  }
  static Maybe<T> GetCommon(const char* aMatch, int& aArgc, char** aArgv,
                            const CheckArgFlag aFlags);

  const char* Name() { return sName; };

  void Put(T aValue, ChildProcessArgs& aArgs) {
    return PutCommon(sName, std::move(aValue), aArgs);
  }
  static void PutCommon(const char* aName, T aValue, ChildProcessArgs& aArgs);

  const char* sName;
  const char* sMatch;
};

/// Get()

inline Maybe<uint64_t> ParseIntArgument(std::string_view aStr) {
  uint64_t conv = 0;
  const char* end = aStr.data() + aStr.size();
  auto [ptr, ec] = std::from_chars(aStr.data(), end, conv);
  if (ec == std::errc() && ptr == end) {
    return Some(conv);
  }
  return Nothing();
}

template <>
inline Maybe<const char*> CommandLineArg<const char*>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags) {
  MOZ_ASSERT(aArgv, "aArgv must be initialized before CheckArg()");
  const char* rv = nullptr;
  if (ARG_FOUND == CheckArg(aArgc, aArgv, aMatch, &rv, aFlags)) {
    return Some(rv);
  }
  return Nothing();
}

template <>
inline Maybe<bool> CommandLineArg<bool>::GetCommon(const char* aMatch,
                                                   int& aArgc, char** aArgv,
                                                   const CheckArgFlag aFlags) {
  MOZ_ASSERT(aArgv, "aArgv must be initialized before CheckArg()");
  if (ARG_FOUND ==
      CheckArg(aArgc, aArgv, aMatch, (const char**)nullptr, aFlags)) {
    return Some(true);
  }
  return Nothing();
}

template <>
inline Maybe<uint64_t> CommandLineArg<uint64_t>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags) {
  if (Maybe<const char*> arg = CommandLineArg<const char*>::GetCommon(
          aMatch, aArgc, aArgv, aFlags)) {
    return ParseIntArgument(*arg);
  }
  return Nothing();
}

template <>
inline Maybe<uint32_t> CommandLineArg<uint32_t>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags) {
  return CommandLineArg<uint64_t>::GetCommon(aMatch, aArgc, aArgv, aFlags);
}

template <>
Maybe<UniqueFileHandle> CommandLineArg<UniqueFileHandle>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags);

#ifdef XP_DARWIN
template <>
Maybe<UniqueMachSendRight> CommandLineArg<UniqueMachSendRight>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags);
template <>
Maybe<UniqueMachReceiveRight> CommandLineArg<UniqueMachReceiveRight>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags);
#endif  // XP_DARWIN

template <>
Maybe<mozilla::ipc::ReadOnlySharedMemoryHandle>
CommandLineArg<mozilla::ipc::ReadOnlySharedMemoryHandle>::GetCommon(
    const char* aMatch, int& aArgc, char** aArgv, const CheckArgFlag aFlags);

/// Put()

template <>
inline void CommandLineArg<const char*>::PutCommon(const char* aName,
                                                   const char* aValue,
                                                   ChildProcessArgs& aArgs) {
  aArgs.mArgs.push_back(aName);
  aArgs.mArgs.push_back(aValue);
}

template <>
inline void CommandLineArg<bool>::PutCommon(const char* aName, bool aValue,
                                            ChildProcessArgs& aArgs) {
  if (aValue) {
    aArgs.mArgs.push_back(aName);
  }
}

template <>
inline void CommandLineArg<uint64_t>::PutCommon(const char* aName,
                                                uint64_t aValue,
                                                ChildProcessArgs& aArgs) {
  aArgs.mArgs.push_back(aName);
  aArgs.mArgs.push_back(std::to_string(aValue));
}

template <>
inline void CommandLineArg<uint32_t>::PutCommon(const char* aName,
                                                uint32_t aValue,
                                                ChildProcessArgs& aArgs) {
  CommandLineArg<uint64_t>::PutCommon(aName, aValue, aArgs);
}

template <>
void CommandLineArg<UniqueFileHandle>::PutCommon(const char* aName,
                                                 UniqueFileHandle aValue,
                                                 ChildProcessArgs& aArgs);

#ifdef XP_DARWIN
template <>
void CommandLineArg<UniqueMachSendRight>::PutCommon(const char* aName,
                                                    UniqueMachSendRight aValue,
                                                    ChildProcessArgs& aArgs);
template <>
void CommandLineArg<UniqueMachReceiveRight>::PutCommon(
    const char* aName, UniqueMachReceiveRight aValue, ChildProcessArgs& aArgs);
#endif  // XP_DARWIN

template <>
void CommandLineArg<mozilla::ipc::ReadOnlySharedMemoryHandle>::PutCommon(
    const char* aName, mozilla::ipc::ReadOnlySharedMemoryHandle aValue,
    ChildProcessArgs& aArgs);

#if defined(__GNUC__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wunused-variable"
#endif

static CommandLineArg<uint64_t> sParentPid{"-parentPid", "parentpid"};
static CommandLineArg<const char*> sInitialChannelID{"-initialChannelId",
                                                     "initialchannelid"};
static CommandLineArg<const char*> sParentBuildID{"-parentBuildID",
                                                  "parentbuildid"};
static CommandLineArg<const char*> sAppDir{"-appDir", "appdir"};
static CommandLineArg<const char*> sGREOmni{"-greomni", "greomni"};
static CommandLineArg<const char*> sAppOmni{"-appomni", "appomni"};
static CommandLineArg<const char*> sProfile{"-profile", "profile"};

static CommandLineArg<UniqueFileHandle> sIPCHandle{"-ipcHandle", "ipchandle"};
#if defined(XP_DARWIN)
static CommandLineArg<UniqueMachSendRight> sIPCPort{"-ipcPort", "ipcport"};
#endif

static CommandLineArg<mozilla::ipc::ReadOnlySharedMemoryHandle> sJsInitHandle{
    "-jsInitHandle", "jsinithandle"};
static CommandLineArg<mozilla::ipc::ReadOnlySharedMemoryHandle> sPrefsHandle{
    "-prefsHandle", "prefshandle"};
static CommandLineArg<mozilla::ipc::ReadOnlySharedMemoryHandle> sPrefMapHandle{
    "-prefMapHandle", "prefmaphandle"};

static CommandLineArg<uint64_t> sSandboxingKind{"-sandboxingKind",
                                                "sandboxingkind"};

static CommandLineArg<bool> sSafeMode{"-safeMode", "safemode"};
static CommandLineArg<bool> sDisableJit{"-disableJit", "disablejit"};

static CommandLineArg<bool> sIsForBrowser{"-isForBrowser", "isforbrowser"};
static CommandLineArg<bool> sNotForBrowser{"-notForBrowser", "notforbrowser"};

static CommandLineArg<const char*> sPluginPath{"-pluginPath", "pluginpath"};
static CommandLineArg<bool> sPluginNativeEvent{"-pluginNativeEvent",
                                               "pluginnativeevent"};
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_IOS)
static CommandLineArg<const char*> sCrashReporter{"-crashReporter",
                                                  "crashreporter"};
#else
static CommandLineArg<UniqueFileHandle> sCrashReporter{"-crashReporter",
                                                       "crashreporter"};
#endif
#if defined(XP_DARWIN)
static CommandLineArg<UniqueMachSendRight> sCrashHelperSend{"-crashHelperSend",
                                                            "crashhelpersend"};
static CommandLineArg<UniqueMachReceiveRight> sCrashHelperRecv{
    "-crashHelperRecv", "crashhelperrecv"};
#else
static CommandLineArg<UniqueFileHandle> sCrashHelper{"-crashHelper",
                                                     "crashhelper"};
#endif  // XP_DARWIN
static CommandLineArg<uint64_t> sCrashHelperPid{"-crashHelperPid",
                                                "crashhelperpid"};

#if defined(XP_WIN)
#  if defined(MOZ_SANDBOX)
static CommandLineArg<bool> sWin32kLockedDown{"-win32kLockedDown",
                                              "win32klockeddown"};
#  endif  // defined(MOZ_SANDBOX)
static CommandLineArg<bool> sDisableDynamicDllBlocklist{
    "-disableDynamicBlocklist", "disabledynamicblocklist"};
#endif  // defined(XP_WIN)

#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
static CommandLineArg<UniqueFileHandle> sSandboxReporter{"-sandboxReporter",
                                                         "sandboxreporter"};
static CommandLineArg<UniqueFileHandle> sChrootClient{"-chrootClient",
                                                      "chrootclient"};
#endif

#ifdef MOZ_ENABLE_FORKSERVER
static CommandLineArg<UniqueFileHandle> sSignalPipe{"-signalPipe",
                                                    "signalpipe"};
#endif

#if defined(__GNUC__)
#  pragma GCC diagnostic pop
#endif

}  // namespace geckoargs

}  // namespace mozilla

#endif  // mozilla_GeckoArgs_h
