tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XPCShellImpl.cpp (42923B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsXULAppAPI.h"
      8 #include "jsapi.h"
      9 #include "jsfriendapi.h"
     10 #include "js/Array.h"             // JS::NewArrayObject
     11 #include "js/CallAndConstruct.h"  // JS_CallFunctionValue
     12 #include "js/CharacterEncoding.h"
     13 #include "js/CompilationAndEvaluation.h"  // JS::Evaluate
     14 #include "js/ContextOptions.h"
     15 #include "js/Printf.h"
     16 #include "js/PropertyAndElement.h"  // JS_DefineElement, JS_DefineFunctions, JS_DefineProperty
     17 #include "js/PropertySpec.h"
     18 #include "js/SourceText.h"  // JS::SourceText
     19 #include "mozilla/ChaosMode.h"
     20 #include "mozilla/dom/AutoEntryScript.h"
     21 #include "mozilla/dom/ScriptSettings.h"
     22 #include "mozilla/IOInterposer.h"
     23 #include "mozilla/Preferences.h"
     24 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
     25 #include "nsServiceManagerUtils.h"
     26 #include "nsComponentManagerUtils.h"
     27 #include "nsExceptionHandler.h"
     28 #include "nsIServiceManager.h"
     29 #include "nsIFile.h"
     30 #include "nsString.h"
     31 #include "nsIDirectoryService.h"
     32 #include "nsDirectoryServiceDefs.h"
     33 #include "nsAppDirectoryServiceDefs.h"
     34 #include "nscore.h"
     35 #include "nsArrayEnumerator.h"
     36 #include "nsCOMArray.h"
     37 #include "nsDirectoryServiceUtils.h"
     38 #include "nsCOMPtr.h"
     39 #include "nsJSPrincipals.h"
     40 #include "nsJSUtils.h"
     41 #include "xpcpublic.h"
     42 #include "xpcprivate.h"
     43 #include "SystemGlobal.h"
     44 #include "nsIScriptSecurityManager.h"
     45 #include "nsIPrincipal.h"
     46 #include "nsJSUtils.h"
     47 
     48 #include "nsIXULRuntime.h"
     49 #include "nsIAppStartup.h"
     50 #include "Components.h"
     51 #include "ProfilerControl.h"
     52 
     53 #ifdef ANDROID
     54 #  include <android/log.h>
     55 #  include "XREShellData.h"
     56 #endif
     57 
     58 #ifdef MOZ_WIDGET_ANDROID
     59 #  include "mozilla/java/GeckoThreadWrappers.h"
     60 #endif
     61 
     62 #ifdef XP_WIN
     63 #  include "mozilla/mscom/ProcessRuntime.h"
     64 #  include "mozilla/ScopeExit.h"
     65 #  include "mozilla/WinDllServices.h"
     66 #  include "mozilla/WindowsBCryptInitialization.h"
     67 #  include <windows.h>
     68 #  if defined(MOZ_SANDBOX)
     69 #    include "XREShellData.h"
     70 #    include "sandboxBroker.h"
     71 #  endif
     72 #endif
     73 
     74 #ifdef MOZ_CODE_COVERAGE
     75 #  include "mozilla/CodeCoverageHandler.h"
     76 #endif
     77 
     78 // all this crap is needed to do the interactive shell stuff
     79 #include <stdlib.h>
     80 #include <errno.h>
     81 #ifdef HAVE_IO_H
     82 #  include <io.h> /* for isatty() */
     83 #endif
     84 #ifdef HAVE_UNISTD_H
     85 #  include <unistd.h> /* for isatty() */
     86 #endif
     87 
     88 #ifdef ENABLE_TESTS
     89 #  include "xpctest_private.h"
     90 #endif
     91 
     92 // Fuzzing support for XPC runtime fuzzing
     93 #ifdef FUZZING_INTERFACES
     94 #  include "xpcrtfuzzing/xpcrtfuzzing.h"
     95 #  include "XREShellData.h"
     96 MOZ_RUNINIT static bool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG");
     97 MOZ_RUNINIT static bool fuzzHaveModule = !!getenv("FUZZER");
     98 #endif  // FUZZING_INTERFACES
     99 
    100 using namespace mozilla;
    101 using namespace JS;
    102 using mozilla::dom::AutoEntryScript;
    103 using mozilla::dom::AutoJSAPI;
    104 
    105 class XPCShellDirProvider : public nsIDirectoryServiceProvider2 {
    106 public:
    107  NS_DECL_ISUPPORTS_INHERITED
    108  NS_DECL_NSIDIRECTORYSERVICEPROVIDER
    109  NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
    110 
    111  XPCShellDirProvider() = default;
    112  ~XPCShellDirProvider() = default;
    113 
    114  // The platform resource folder
    115  void SetGREDirs(nsIFile* greDir);
    116  void ClearGREDirs() {
    117    mGREDir = nullptr;
    118    mGREBinDir = nullptr;
    119  }
    120  // The application resource folder
    121  void SetAppDir(nsIFile* appFile);
    122  void ClearAppDir() { mAppDir = nullptr; }
    123  // The app executable
    124  void SetAppFile(nsIFile* appFile);
    125  void ClearAppFile() { mAppFile = nullptr; }
    126 
    127 private:
    128  nsCOMPtr<nsIFile> mGREDir;
    129  nsCOMPtr<nsIFile> mGREBinDir;
    130  nsCOMPtr<nsIFile> mAppDir;
    131  nsCOMPtr<nsIFile> mAppFile;
    132 };
    133 
    134 #define EXITCODE_RUNTIME_ERROR 3
    135 #define EXITCODE_FILE_NOT_FOUND 4
    136 
    137 static FILE* gOutFile = nullptr;
    138 static FILE* gErrFile = nullptr;
    139 static FILE* gInFile = nullptr;
    140 
    141 static int gExitCode = 0;
    142 static bool gQuitting = false;
    143 static bool reportWarnings = true;
    144 static bool compileOnly = false;
    145 
    146 static JSPrincipals* gJSPrincipals = nullptr;
    147 static nsAutoString* gWorkingDirectory = nullptr;
    148 
    149 static bool GetLocationProperty(JSContext* cx, unsigned argc, Value* vp) {
    150  CallArgs args = CallArgsFromVp(argc, vp);
    151  if (!args.thisv().isObject()) {
    152    JS_ReportErrorASCII(cx, "Unexpected this value for GetLocationProperty");
    153    return false;
    154  }
    155 #if !defined(XP_WIN) && !defined(XP_UNIX)
    156  // XXX: your platform should really implement this
    157  return false;
    158 #else
    159  JS::AutoFilename filename;
    160  if (JS::DescribeScriptedCaller(&filename, cx) && filename.get()) {
    161    NS_ConvertUTF8toUTF16 filenameString(filename.get());
    162 
    163 #  if defined(XP_WIN)
    164    // replace forward slashes with backslashes,
    165    // since nsLocalFileWin chokes on them
    166    char16_t* start = filenameString.BeginWriting();
    167    char16_t* end = filenameString.EndWriting();
    168 
    169    while (start != end) {
    170      if (*start == L'/') {
    171        *start = L'\\';
    172      }
    173      start++;
    174    }
    175 #  endif
    176 
    177    nsCOMPtr<nsIFile> location;
    178    (void)NS_NewLocalFile(filenameString, getter_AddRefs(location));
    179 
    180    if (!location && gWorkingDirectory) {
    181      // could be a relative path, try appending it to the cwd
    182      // and then normalize
    183      nsAutoString absolutePath(*gWorkingDirectory);
    184      absolutePath.Append(filenameString);
    185 
    186      (void)NS_NewLocalFile(absolutePath, getter_AddRefs(location));
    187    }
    188 
    189    if (location) {
    190      bool symlink;
    191      // don't normalize symlinks, because that's kind of confusing
    192      if (NS_SUCCEEDED(location->IsSymlink(&symlink)) && !symlink)
    193        location->Normalize();
    194      RootedObject locationObj(cx);
    195      RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
    196      nsresult rv = nsXPConnect::XPConnect()->WrapNative(
    197          cx, scope, location, NS_GET_IID(nsIFile), locationObj.address());
    198      if (NS_SUCCEEDED(rv) && locationObj) {
    199        args.rval().setObject(*locationObj);
    200      }
    201    }
    202  }
    203 
    204  return true;
    205 #endif
    206 }
    207 
    208 static bool GetLine(JSContext* cx, char* bufp, FILE* file, const char* prompt) {
    209  fputs(prompt, gOutFile);
    210  fflush(gOutFile);
    211 
    212  char line[4096] = {'\0'};
    213  while (true) {
    214    if (fgets(line, sizeof line, file)) {
    215      strcpy(bufp, line);
    216      return true;
    217    }
    218    if (errno != EINTR) {
    219      return false;
    220    }
    221  }
    222 }
    223 
    224 static bool ReadLine(JSContext* cx, unsigned argc, Value* vp) {
    225  CallArgs args = CallArgsFromVp(argc, vp);
    226 
    227  // While 4096 might be quite arbitrary, this is something to be fixed in
    228  // bug 105707. It is also the same limit as in ProcessFile.
    229  char buf[4096];
    230  RootedString str(cx);
    231 
    232  /* If a prompt was specified, construct the string */
    233  if (args.length() > 0) {
    234    str = JS::ToString(cx, args[0]);
    235    if (!str) {
    236      return false;
    237    }
    238  } else {
    239    str = JS_GetEmptyString(cx);
    240  }
    241 
    242  /* Get a line from the infile */
    243  JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str);
    244  if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.get())) {
    245    return false;
    246  }
    247 
    248  /* Strip newline character added by GetLine() */
    249  unsigned int buflen = strlen(buf);
    250  if (buflen == 0) {
    251    if (feof(gInFile)) {
    252      args.rval().setNull();
    253      return true;
    254    }
    255  } else if (buf[buflen - 1] == '\n') {
    256    --buflen;
    257  }
    258 
    259  /* Turn buf into a JSString */
    260  str = JS_NewStringCopyN(cx, buf, buflen);
    261  if (!str) {
    262    return false;
    263  }
    264 
    265  args.rval().setString(str);
    266  return true;
    267 }
    268 
    269 static bool Print(JSContext* cx, unsigned argc, Value* vp) {
    270  CallArgs args = CallArgsFromVp(argc, vp);
    271  args.rval().setUndefined();
    272 
    273 #ifdef FUZZING_INTERFACES
    274  if (fuzzHaveModule && !fuzzDoDebug) {
    275    // When fuzzing and not debugging, suppress any print() output,
    276    // as it slows down fuzzing and makes libFuzzer's output hard
    277    // to read.
    278    return true;
    279  }
    280 #endif  // FUZZING_INTERFACES
    281 
    282  RootedString str(cx);
    283  nsAutoCString utf8output;
    284 
    285  for (unsigned i = 0; i < args.length(); i++) {
    286    str = ToString(cx, args[i]);
    287    if (!str) {
    288      return false;
    289    }
    290 
    291    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
    292    if (!utf8str) {
    293      return false;
    294    }
    295 
    296    if (i) {
    297      utf8output.Append(' ');
    298    }
    299    utf8output.Append(utf8str.get(), strlen(utf8str.get()));
    300  }
    301  utf8output.Append('\n');
    302  fputs(utf8output.get(), gOutFile);
    303  fflush(gOutFile);
    304  return true;
    305 }
    306 
    307 static bool Dump(JSContext* cx, unsigned argc, Value* vp) {
    308  CallArgs args = CallArgsFromVp(argc, vp);
    309  args.rval().setUndefined();
    310 
    311  if (!args.length()) {
    312    return true;
    313  }
    314 
    315  RootedString str(cx, ToString(cx, args[0]));
    316  if (!str) {
    317    return false;
    318  }
    319 
    320  JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
    321  if (!utf8str) {
    322    return false;
    323  }
    324 
    325 #ifdef ANDROID
    326  __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
    327 #endif
    328 #ifdef XP_WIN
    329  if (IsDebuggerPresent()) {
    330    nsAutoJSString wstr;
    331    if (!wstr.init(cx, str)) {
    332      return false;
    333    }
    334    OutputDebugStringW(wstr.get());
    335  }
    336 #endif
    337  fputs(utf8str.get(), gOutFile);
    338  fflush(gOutFile);
    339  return true;
    340 }
    341 
    342 static bool Load(JSContext* cx, unsigned argc, Value* vp) {
    343  CallArgs args = CallArgsFromVp(argc, vp);
    344 
    345  JS::RootedObject thisObject(cx);
    346  if (!args.computeThis(cx, &thisObject)) {
    347    return false;
    348  }
    349  if (!JS_IsGlobalObject(thisObject)) {
    350    JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
    351    return false;
    352  }
    353 
    354  RootedString str(cx);
    355  for (unsigned i = 0; i < args.length(); i++) {
    356    str = ToString(cx, args[i]);
    357    if (!str) {
    358      return false;
    359    }
    360    JS::UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
    361    if (!filename) {
    362      return false;
    363    }
    364    JS::CompileOptions options(cx);
    365    options.setIsRunOnce(true).setSkipFilenameValidation(true);
    366    JS::Rooted<JSScript*> script(
    367        cx, JS::CompileUtf8Path(cx, options, filename.get()));
    368    if (!script) {
    369      return false;
    370    }
    371 
    372    if (!compileOnly) {
    373      if (!JS_ExecuteScript(cx, script)) {
    374        return false;
    375      }
    376    }
    377  }
    378  args.rval().setUndefined();
    379  return true;
    380 }
    381 
    382 static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
    383  CallArgs args = CallArgsFromVp(argc, vp);
    384 
    385  gExitCode = 0;
    386  if (!ToInt32(cx, args.get(0), &gExitCode)) {
    387    return false;
    388  }
    389 
    390  gQuitting = true;
    391  JS::ReportUncatchableException(cx);
    392  return false;
    393 }
    394 
    395 static bool DumpXPC(JSContext* cx, unsigned argc, Value* vp) {
    396  JS::CallArgs args = CallArgsFromVp(argc, vp);
    397 
    398  uint16_t depth = 2;
    399  if (args.length() > 0) {
    400    if (!JS::ToUint16(cx, args[0], &depth)) {
    401      return false;
    402    }
    403  }
    404 
    405  nsXPConnect::XPConnect()->DebugDump(int16_t(depth));
    406  args.rval().setUndefined();
    407  return true;
    408 }
    409 
    410 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
    411  CallArgs args = CallArgsFromVp(argc, vp);
    412 
    413  JS_GC(cx);
    414 
    415  args.rval().setUndefined();
    416  return true;
    417 }
    418 
    419 #ifdef JS_GC_ZEAL
    420 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
    421  CallArgs args = CallArgsFromVp(argc, vp);
    422  uint32_t zeal;
    423  if (!ToUint32(cx, args.get(0), &zeal)) {
    424    return false;
    425  }
    426 
    427  JS::SetGCZeal(cx, uint8_t(zeal), JS::ShellDefaultGCZealFrequency);
    428  args.rval().setUndefined();
    429  return true;
    430 }
    431 #endif
    432 
    433 static bool SendCommand(JSContext* cx, unsigned argc, Value* vp) {
    434  CallArgs args = CallArgsFromVp(argc, vp);
    435 
    436  if (args.length() == 0) {
    437    JS_ReportErrorASCII(cx, "Function takes at least one argument!");
    438    return false;
    439  }
    440 
    441  RootedString str(cx, ToString(cx, args[0]));
    442  if (!str) {
    443    JS_ReportErrorASCII(cx, "Could not convert argument 1 to string!");
    444    return false;
    445  }
    446 
    447  if (args.get(1).isObject() && !JS_ObjectIsFunction(&args[1].toObject())) {
    448    JS_ReportErrorASCII(cx, "Could not convert argument 2 to function!");
    449    return false;
    450  }
    451 
    452  if (!XRE_SendTestShellCommand(
    453          cx, str, args.length() > 1 ? args[1].address() : nullptr)) {
    454    JS_ReportErrorASCII(cx, "Couldn't send command!");
    455    return false;
    456  }
    457 
    458  args.rval().setUndefined();
    459  return true;
    460 }
    461 
    462 static bool Options(JSContext* cx, unsigned argc, Value* vp) {
    463  JS::CallArgs args = CallArgsFromVp(argc, vp);
    464 
    465  RootedString str(cx);
    466  JS::UniqueChars opt;
    467  if (args.length() > 0) {
    468    str = ToString(cx, args[0]);
    469    if (!str) {
    470      return false;
    471    }
    472 
    473    opt = JS_EncodeStringToUTF8(cx, str);
    474    if (!opt) {
    475      return false;
    476    }
    477 
    478    JS_ReportErrorUTF8(cx, "unknown option name '%s'.", opt.get());
    479    return false;
    480  }
    481 
    482  args.rval().setString(JS_GetEmptyString(cx));
    483  return true;
    484 }
    485 
    486 static PersistentRootedValue* sScriptedInterruptCallback = nullptr;
    487 
    488 static bool XPCShellInterruptCallback(JSContext* cx) {
    489  MOZ_ASSERT(sScriptedInterruptCallback->initialized());
    490  RootedValue callback(cx, *sScriptedInterruptCallback);
    491 
    492  // If no interrupt callback was set by script, no-op.
    493  if (callback.isUndefined()) {
    494    return true;
    495  }
    496 
    497  MOZ_ASSERT(js::IsFunctionObject(&callback.toObject()));
    498 
    499  JSAutoRealm ar(cx, &callback.toObject());
    500  RootedValue rv(cx);
    501  if (!JS_CallFunctionValue(cx, nullptr, callback,
    502                            JS::HandleValueArray::empty(), &rv) ||
    503      !rv.isBoolean()) {
    504    NS_WARNING("Scripted interrupt callback failed! Terminating script.");
    505    JS_ClearPendingException(cx);
    506    return false;
    507  }
    508 
    509  return rv.toBoolean();
    510 }
    511 
    512 static bool SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) {
    513  MOZ_ASSERT(sScriptedInterruptCallback->initialized());
    514 
    515  // Sanity-check args.
    516  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    517  if (args.length() != 1) {
    518    JS_ReportErrorASCII(cx, "Wrong number of arguments");
    519    return false;
    520  }
    521 
    522  // Allow callers to remove the interrupt callback by passing undefined.
    523  if (args[0].isUndefined()) {
    524    *sScriptedInterruptCallback = UndefinedValue();
    525    return true;
    526  }
    527 
    528  // Otherwise, we should have a function object.
    529  if (!args[0].isObject() || !js::IsFunctionObject(&args[0].toObject())) {
    530    JS_ReportErrorASCII(cx, "Argument must be a function");
    531    return false;
    532  }
    533 
    534  *sScriptedInterruptCallback = args[0];
    535 
    536  return true;
    537 }
    538 
    539 static bool SimulateNoScriptActivity(JSContext* cx, unsigned argc, Value* vp) {
    540  // Sanity-check args.
    541  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    542  if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
    543    JS_ReportErrorASCII(cx, "Expected a positive integer argument");
    544    return false;
    545  }
    546 
    547  // This mimics mozilla::SpinEventLoopUntil but instead of spinning the
    548  // event loop we sleep, to make sure we don't run script.
    549  xpc::AutoScriptActivity asa(false);
    550  PR_Sleep(PR_SecondsToInterval(args[0].toInt32()));
    551 
    552  args.rval().setUndefined();
    553  return true;
    554 }
    555 
    556 #ifdef ANDROID
    557 static bool ChangeTestShellDir(JSContext* cx, unsigned argc, Value* vp) {
    558  // This method should only be used by testing/xpcshell/head.js to change to
    559  // the correct directory on Android Remote XPCShell tests.
    560  //
    561  // TODO: Bug 1801725 - Find a more ergonomic way to do this than exposing
    562  // identical methods in XPCShellEnvironment and XPCShellImpl to chdir on
    563  // android for Remote XPCShell tests on Android.
    564  CallArgs args = CallArgsFromVp(argc, vp);
    565 
    566  if (args.length() != 1) {
    567    JS_ReportErrorASCII(cx, "changeTestShellDir() takes one argument");
    568    return false;
    569  }
    570 
    571  nsAutoJSCString path;
    572  if (!path.init(cx, args[0])) {
    573    JS_ReportErrorASCII(
    574        cx, "changeTestShellDir(): could not convert argument 1 to string");
    575    return false;
    576  }
    577 
    578  if (chdir(path.get())) {
    579    JS_ReportErrorASCII(cx, "changeTestShellDir(): could not change directory");
    580    return false;
    581  }
    582 
    583  return true;
    584 }
    585 #endif
    586 
    587 #ifdef ENABLE_TESTS
    588 static bool RegisterXPCTestComponents(JSContext* cx, unsigned argc, Value* vp) {
    589  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    590  if (args.length() != 0) {
    591    JS_ReportErrorASCII(cx, "Wrong number of arguments");
    592    return false;
    593  }
    594  nsresult rv = xpcTestRegisterComponents();
    595  if (NS_FAILED(rv)) {
    596    XPCThrower::Throw(rv, cx);
    597    return false;
    598  }
    599  return true;
    600 }
    601 #endif
    602 
    603 static const JSFunctionSpec glob_functions[] = {
    604    // clang-format off
    605    JS_FN("print",           Print,          0,0),
    606    JS_FN("readline",        ReadLine,       1,0),
    607    JS_FN("load",            Load,           1,0),
    608    JS_FN("quit",            Quit,           0,0),
    609    JS_FN("dumpXPC",         DumpXPC,        1,0),
    610    JS_FN("dump",            Dump,           1,0),
    611    JS_FN("gc",              GC,             0,0),
    612 #ifdef JS_GC_ZEAL
    613    JS_FN("gczeal",          GCZeal,         1,0),
    614 #endif
    615    JS_FN("options",         Options,        0,0),
    616    JS_FN("sendCommand",     SendCommand,    1,0),
    617    JS_FN("atob",            xpc::Atob,      1,0),
    618    JS_FN("btoa",            xpc::Btoa,      1,0),
    619    JS_FN("setInterruptCallback", SetInterruptCallback, 1,0),
    620    JS_FN("simulateNoScriptActivity", SimulateNoScriptActivity, 1,0),
    621 #ifdef ANDROID
    622    JS_FN("changeTestShellDir", ChangeTestShellDir, 1,0),
    623 #endif
    624 #ifdef ENABLE_TESTS
    625    JS_FN("registerXPCTestComponents", RegisterXPCTestComponents, 0, 0),
    626 #endif
    627    JS_FS_END
    628    // clang-format on
    629 };
    630 
    631 /***************************************************************************/
    632 
    633 typedef enum JSShellErrNum {
    634 #define MSG_DEF(name, number, count, exception, format) name = number,
    635 #include "jsshell.msg"
    636 #undef MSG_DEF
    637  JSShellErr_Limit
    638 } JSShellErrNum;
    639 
    640 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
    641 #define MSG_DEF(name, number, count, exception, format) {#name, format, count},
    642 #include "jsshell.msg"
    643 #undef MSG_DEF
    644 };
    645 
    646 static const JSErrorFormatString* my_GetErrorMessage(
    647    void* userRef, const unsigned errorNumber) {
    648  if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) {
    649    return nullptr;
    650  }
    651 
    652  return &jsShell_ErrorFormatString[errorNumber];
    653 }
    654 
    655 static bool ProcessUtf8Line(AutoJSAPI& jsapi, const char* buffer,
    656                            int startline) {
    657  JSContext* cx = jsapi.cx();
    658  JS::CompileOptions options(cx);
    659  options.setFileAndLine("typein", startline)
    660      .setIsRunOnce(true)
    661      .setSkipFilenameValidation(true);
    662 
    663  JS::SourceText<mozilla::Utf8Unit> srcBuf;
    664  if (!srcBuf.init(cx, buffer, strlen(buffer), JS::SourceOwnership::Borrowed)) {
    665    return false;
    666  }
    667 
    668  JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf));
    669  if (!script) {
    670    return false;
    671  }
    672  if (compileOnly) {
    673    return true;
    674  }
    675 
    676  JS::RootedValue result(cx);
    677  if (!JS_ExecuteScript(cx, script, &result)) {
    678    return false;
    679  }
    680 
    681  if (result.isUndefined()) {
    682    return true;
    683  }
    684 
    685  RootedString str(cx, JS::ToString(cx, result));
    686  if (!str) {
    687    return false;
    688  }
    689 
    690  JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
    691  if (!bytes) {
    692    return false;
    693  }
    694 
    695  fprintf(gOutFile, "%s\n", bytes.get());
    696  return true;
    697 }
    698 
    699 static bool ProcessFile(AutoJSAPI& jsapi, const char* filename, FILE* file,
    700                        bool forceTTY) {
    701  JSContext* cx = jsapi.cx();
    702  JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
    703  MOZ_ASSERT(global);
    704 
    705  if (forceTTY) {
    706    file = stdin;
    707  } else if (!isatty(fileno(file))) {
    708    /*
    709     * It's not interactive - just execute it.
    710     *
    711     * Support the UNIX #! shell hack; gobble the first line if it starts
    712     * with '#'.
    713     */
    714    int ch = fgetc(file);
    715    if (ch == '#') {
    716      while ((ch = fgetc(file)) != EOF) {
    717        if (ch == '\n' || ch == '\r') {
    718          break;
    719        }
    720      }
    721    }
    722    ungetc(ch, file);
    723 
    724    JS::UniqueChars filenameUtf8 = JS::EncodeNarrowToUtf8(jsapi.cx(), filename);
    725    if (!filenameUtf8) {
    726      return false;
    727    }
    728 
    729    JS::RootedScript script(cx);
    730    JS::RootedValue unused(cx);
    731    JS::CompileOptions options(cx);
    732    options.setFileAndLine(filenameUtf8.get(), 1)
    733        .setIsRunOnce(true)
    734        .setNoScriptRval(true)
    735        .setSkipFilenameValidation(true);
    736    script = JS::CompileUtf8File(cx, options, file);
    737    if (!script) {
    738      return false;
    739    }
    740    return compileOnly || JS_ExecuteScript(cx, script, &unused);
    741  }
    742 
    743  /* It's an interactive filehandle; drop into read-eval-print loop. */
    744  int lineno = 1;
    745  bool hitEOF = false;
    746  do {
    747    char buffer[4096];
    748    char* bufp = buffer;
    749    *bufp = '\0';
    750 
    751    /*
    752     * Accumulate lines until we get a 'compilable unit' - one that either
    753     * generates an error (before running out of source) or that compiles
    754     * cleanly.  This should be whenever we get a complete statement that
    755     * coincides with the end of a line.
    756     */
    757    int startline = lineno;
    758    do {
    759      if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
    760        hitEOF = true;
    761        break;
    762      }
    763      bufp += strlen(bufp);
    764      lineno++;
    765    } while (
    766        !JS_Utf8BufferIsCompilableUnit(cx, global, buffer, strlen(buffer)));
    767 
    768    if (!ProcessUtf8Line(jsapi, buffer, startline)) {
    769      jsapi.ReportException();
    770    }
    771  } while (!hitEOF && !gQuitting);
    772 
    773  fprintf(gOutFile, "\n");
    774  return true;
    775 }
    776 
    777 static bool Process(AutoJSAPI& jsapi, const char* filename, bool forceTTY) {
    778  FILE* file;
    779 
    780  if (forceTTY || !filename || strcmp(filename, "-") == 0) {
    781    file = stdin;
    782  } else {
    783    file = fopen(filename, "r");
    784    if (!file) {
    785      /*
    786       * Use Latin1 variant here because the encoding of the return value
    787       * of strerror function can be non-UTF-8.
    788       */
    789      JS_ReportErrorNumberLatin1(jsapi.cx(), my_GetErrorMessage, nullptr,
    790                                 JSSMSG_CANT_OPEN, filename, strerror(errno));
    791      gExitCode = EXITCODE_FILE_NOT_FOUND;
    792      return false;
    793    }
    794  }
    795 
    796  bool ok = ProcessFile(jsapi, filename, file, forceTTY);
    797  if (file != stdin) {
    798    fclose(file);
    799  }
    800  return ok;
    801 }
    802 
    803 static int usage() {
    804  fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
    805  fprintf(
    806      gErrFile,
    807      "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-WwxiCmIp] "
    808      "[-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
    809  return 2;
    810 }
    811 
    812 static bool printUsageAndSetExitCode() {
    813  gExitCode = usage();
    814  return false;
    815 }
    816 
    817 static bool ProcessArgs(AutoJSAPI& jsapi, char** argv, int argc,
    818                        XPCShellDirProvider* aDirProvider) {
    819  JSContext* cx = jsapi.cx();
    820  const char rcfilename[] = "xpcshell.js";
    821  FILE* rcfile;
    822  int rootPosition;
    823  JS::Rooted<JSObject*> argsObj(cx);
    824  char* filename = nullptr;
    825  bool isInteractive = true;
    826  bool forceTTY = false;
    827 
    828  rcfile = fopen(rcfilename, "r");
    829  if (rcfile) {
    830    printf("[loading '%s'...]\n", rcfilename);
    831    bool ok = ProcessFile(jsapi, rcfilename, rcfile, false);
    832    fclose(rcfile);
    833    if (!ok) {
    834      return false;
    835    }
    836  }
    837 
    838  JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
    839 
    840  /*
    841   * Scan past all optional arguments so we can create the arguments object
    842   * before processing any -f options, which must interleave properly with
    843   * -v and -w options.  This requires two passes, and without getopt, we'll
    844   * have to keep the option logic here and in the second for loop in sync.
    845   * First of all, find out the first argument position which will be passed
    846   * as a script file to be executed.
    847   */
    848  for (rootPosition = 0; rootPosition < argc; rootPosition++) {
    849    if (argv[rootPosition][0] != '-' || argv[rootPosition][1] == '\0') {
    850      ++rootPosition;
    851      break;
    852    }
    853 
    854    bool isPairedFlag =
    855        argv[rootPosition][0] != '\0' &&
    856        (argv[rootPosition][1] == 'v' || argv[rootPosition][1] == 'f' ||
    857         argv[rootPosition][1] == 'e');
    858    if (isPairedFlag && rootPosition < argc - 1) {
    859      ++rootPosition;  // Skip over the 'foo' portion of |-v foo|, |-f foo|, or
    860                       // |-e foo|.
    861    }
    862  }
    863 
    864  /*
    865   * Create arguments early and define it to root it, so it's safe from any
    866   * GC calls nested below, and so it is available to -f <file> arguments.
    867   */
    868  argsObj = JS::NewArrayObject(cx, 0);
    869  if (!argsObj) {
    870    return 1;
    871  }
    872  if (!JS_DefineProperty(cx, global, "arguments", argsObj, 0)) {
    873    return 1;
    874  }
    875 
    876  for (int j = 0, length = argc - rootPosition; j < length; j++) {
    877    RootedString str(cx, JS_NewStringCopyZ(cx, argv[rootPosition++]));
    878    if (!str || !JS_DefineElement(cx, argsObj, j, str, JSPROP_ENUMERATE)) {
    879      return 1;
    880    }
    881  }
    882 
    883  for (int i = 0; i < argc; i++) {
    884    if (argv[i][0] != '-' || argv[i][1] == '\0') {
    885      filename = argv[i++];
    886      isInteractive = false;
    887      break;
    888    }
    889    switch (argv[i][1]) {
    890      case 'W':
    891        reportWarnings = false;
    892        break;
    893      case 'w':
    894        reportWarnings = true;
    895        break;
    896      case 'x':
    897        break;
    898      case 'd':
    899        /* This used to try to turn on the debugger. */
    900        break;
    901      case 'm':
    902        break;
    903      case 'f':
    904        if (++i == argc) {
    905          return printUsageAndSetExitCode();
    906        }
    907        if (!Process(jsapi, argv[i], false)) {
    908          return false;
    909        }
    910        /*
    911         * XXX: js -f foo.js should interpret foo.js and then
    912         * drop into interactive mode, but that breaks test
    913         * harness. Just execute foo.js for now.
    914         */
    915        isInteractive = false;
    916        break;
    917      case 'i':
    918        isInteractive = forceTTY = true;
    919        break;
    920      case 'e': {
    921        RootedValue rval(cx);
    922 
    923        if (++i == argc) {
    924          return printUsageAndSetExitCode();
    925        }
    926 
    927        JS::CompileOptions opts(cx);
    928        opts.setSkipFilenameValidation(true);
    929        opts.setFileAndLine("-e", 1);
    930 
    931        JS::SourceText<mozilla::Utf8Unit> srcBuf;
    932        if (srcBuf.init(cx, argv[i], strlen(argv[i]),
    933                        JS::SourceOwnership::Borrowed)) {
    934          JS::Evaluate(cx, opts, srcBuf, &rval);
    935        }
    936 
    937        isInteractive = false;
    938        break;
    939      }
    940      case 'C':
    941        compileOnly = true;
    942        isInteractive = false;
    943        break;
    944      default:
    945        return printUsageAndSetExitCode();
    946    }
    947  }
    948 
    949  if (filename || isInteractive) {
    950    return Process(jsapi, filename, forceTTY);
    951  }
    952  return true;
    953 }
    954 
    955 /***************************************************************************/
    956 
    957 static bool GetCurrentWorkingDirectory(nsAString& workingDirectory) {
    958 #if !defined(XP_WIN) && !defined(XP_UNIX)
    959  // XXX: your platform should really implement this
    960  return false;
    961 #elif XP_WIN
    962  DWORD requiredLength = GetCurrentDirectoryW(0, nullptr);
    963  workingDirectory.SetLength(requiredLength);
    964  GetCurrentDirectoryW(workingDirectory.Length(),
    965                       (LPWSTR)workingDirectory.BeginWriting());
    966  // we got a trailing null there
    967  workingDirectory.SetLength(requiredLength);
    968  workingDirectory.Replace(workingDirectory.Length() - 1, 1, L'\\');
    969 #elif defined(XP_UNIX)
    970  nsAutoCString cwd;
    971  // 1024 is just a guess at a sane starting value
    972  size_t bufsize = 1024;
    973  char* result = nullptr;
    974  while (result == nullptr) {
    975    cwd.SetLength(bufsize);
    976    result = getcwd(cwd.BeginWriting(), cwd.Length());
    977    if (!result) {
    978      if (errno != ERANGE) {
    979        return false;
    980      }
    981      // need to make the buffer bigger
    982      bufsize *= 2;
    983    }
    984  }
    985  // size back down to the actual string length
    986  cwd.SetLength(strlen(result) + 1);
    987  cwd.Replace(cwd.Length() - 1, 1, '/');
    988  CopyUTF8toUTF16(cwd, workingDirectory);
    989 #endif
    990  return true;
    991 }
    992 
    993 static JSSecurityCallbacks shellSecurityCallbacks;
    994 
    995 int XRE_XPCShellMain(int argc, char** argv, char** envp,
    996                     const XREShellData* aShellData) {
    997  MOZ_ASSERT(aShellData);
    998 
    999  JSContext* cx;
   1000  int result = 0;
   1001  nsresult rv;
   1002 
   1003 #ifdef ANDROID
   1004  gOutFile = aShellData->outFile;
   1005  gErrFile = aShellData->errFile;
   1006 #else
   1007  gOutFile = stdout;
   1008  gErrFile = stderr;
   1009 #endif
   1010  gInFile = stdin;
   1011 
   1012  NS_LogInit();
   1013 
   1014  mozilla::LogModule::Init(argc, argv);
   1015 
   1016  // This guard ensures that all threads that attempt to register themselves
   1017  // with the IOInterposer will be properly tracked.
   1018  mozilla::AutoIOInterposer ioInterposerGuard;
   1019  ioInterposerGuard.Init();
   1020 
   1021  XRE_InitCommandLine(argc, argv);
   1022 
   1023  char aLocal;
   1024  profiler_init(&aLocal);
   1025 
   1026 #ifdef MOZ_ASAN_REPORTER
   1027  PR_SetEnv("MOZ_DISABLE_ASAN_REPORTER=1");
   1028 #endif
   1029 
   1030  if (auto* featureStr = PR_GetEnv("MOZ_CHAOSMODE")) {
   1031    ChaosFeature feature = ChaosFeature::Any;
   1032    long featureInt = strtol(featureStr, nullptr, 16);
   1033    if (featureInt) {
   1034      // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
   1035      feature = static_cast<ChaosFeature>(featureInt);
   1036    }
   1037    ChaosMode::SetChaosFeature(feature);
   1038    ChaosMode::enterChaosMode();
   1039    MOZ_ASSERT(ChaosMode::isActive(ChaosFeature::Any));
   1040  }
   1041 
   1042  if (ChaosMode::isActive(ChaosFeature::Any)) {
   1043    printf_stderr(
   1044        "*** You are running in chaos test mode. See ChaosMode.h. ***\n");
   1045  }
   1046 
   1047 #ifdef XP_WIN
   1048  // Some COM settings are global to the process and must be set before any non-
   1049  // trivial COM is run in the application. Since these settings may affect
   1050  // stability, we should instantiate COM ASAP so that we can ensure that these
   1051  // global settings are configured before anything can interfere.
   1052  mscom::ProcessRuntime mscom;
   1053 
   1054 #  ifdef MOZ_SANDBOX
   1055  nsAutoString binDirPath;
   1056 #  endif
   1057 #endif
   1058 
   1059  // The provider needs to outlive the call to shutting down XPCOM.
   1060  XPCShellDirProvider dirprovider;
   1061 
   1062  {  // Start scoping nsCOMPtrs
   1063    nsCOMPtr<nsIFile> appFile;
   1064    rv = XRE_GetBinaryPath(getter_AddRefs(appFile));
   1065    if (NS_FAILED(rv)) {
   1066      printf("Couldn't find application file.\n");
   1067      return 1;
   1068    }
   1069    nsCOMPtr<nsIFile> appDir;
   1070    rv = appFile->GetParent(getter_AddRefs(appDir));
   1071    if (NS_FAILED(rv)) {
   1072      printf("Couldn't get application directory.\n");
   1073      return 1;
   1074    }
   1075 
   1076 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   1077    // We need the binary directory to initialize the windows sandbox.
   1078    MOZ_ALWAYS_SUCCEEDS(appDir->GetPath(binDirPath));
   1079 #endif
   1080 
   1081    dirprovider.SetAppFile(appFile);
   1082 
   1083    nsCOMPtr<nsIFile> greDir;
   1084    if (argc > 1 && !strcmp(argv[1], "-g")) {
   1085      if (argc < 3) {
   1086        return usage();
   1087      }
   1088 
   1089      rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(greDir));
   1090      if (NS_FAILED(rv)) {
   1091        printf("Couldn't use given GRE dir.\n");
   1092        return 1;
   1093      }
   1094 
   1095      dirprovider.SetGREDirs(greDir);
   1096 
   1097      argc -= 2;
   1098      argv += 2;
   1099    } else {
   1100 #ifdef XP_MACOSX
   1101      // On OSX, the GreD needs to point to Contents/Resources in the .app
   1102      // bundle. Libraries will be loaded at a relative path to GreD, i.e.
   1103      // ../MacOS.
   1104      nsCOMPtr<nsIFile> tmpDir;
   1105      XRE_GetFileFromPath(argv[0], getter_AddRefs(greDir));
   1106      greDir->GetParent(getter_AddRefs(tmpDir));
   1107      tmpDir->Clone(getter_AddRefs(greDir));
   1108      tmpDir->SetNativeLeafName("Resources"_ns);
   1109      bool dirExists = false;
   1110      tmpDir->Exists(&dirExists);
   1111      if (dirExists) {
   1112        greDir = tmpDir.forget();
   1113      }
   1114      dirprovider.SetGREDirs(greDir);
   1115 #else
   1116      nsAutoString workingDir;
   1117      if (!GetCurrentWorkingDirectory(workingDir)) {
   1118        printf("GetCurrentWorkingDirectory failed.\n");
   1119        return 1;
   1120      }
   1121      rv = NS_NewLocalFile(workingDir, getter_AddRefs(greDir));
   1122      if (NS_FAILED(rv)) {
   1123        printf("NS_NewLocalFile failed.\n");
   1124        return 1;
   1125      }
   1126 #endif
   1127    }
   1128 
   1129    if (argc > 1 && !strcmp(argv[1], "-a")) {
   1130      if (argc < 3) {
   1131        return usage();
   1132      }
   1133 
   1134      nsCOMPtr<nsIFile> dir;
   1135      rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(dir));
   1136      if (NS_SUCCEEDED(rv)) {
   1137        appDir = dir;
   1138        dirprovider.SetAppDir(appDir);
   1139      }
   1140      if (NS_FAILED(rv)) {
   1141        printf("Couldn't use given appdir.\n");
   1142        return 1;
   1143      }
   1144      argc -= 2;
   1145      argv += 2;
   1146    }
   1147 
   1148    while (argc > 1 && !strcmp(argv[1], "-r")) {
   1149      if (argc < 3) {
   1150        return usage();
   1151      }
   1152 
   1153      nsCOMPtr<nsIFile> lf;
   1154      rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(lf));
   1155      if (NS_FAILED(rv)) {
   1156        printf("Couldn't get manifest file.\n");
   1157        return 1;
   1158      }
   1159      XRE_AddManifestLocation(NS_APP_LOCATION, lf);
   1160 
   1161      argc -= 2;
   1162      argv += 2;
   1163    }
   1164 
   1165    const char* val = getenv("MOZ_CRASHREPORTER");
   1166    if (val && *val && !CrashReporter::IsDummy()) {
   1167      nsCOMPtr<nsIFile> greBinDir;
   1168      bool persistent = false;
   1169      rv = dirprovider.GetFile(NS_GRE_BIN_DIR, &persistent,
   1170                               getter_AddRefs(greBinDir));
   1171      if (NS_FAILED(rv)) {
   1172        printf("Could not get the GreBinD directory\n");
   1173        return 1;
   1174      }
   1175 
   1176 #if defined(MOZ_WIDGET_ANDROID)
   1177      CrashReporter::SetCrashHelperPipes(
   1178          aShellData->crashChildNotificationSocket,
   1179          aShellData->crashHelperSocket);
   1180 #endif  // defined(MOZ_WIDGET_ANDROID)
   1181 
   1182      rv = CrashReporter::SetExceptionHandler(greBinDir, true);
   1183      if (NS_FAILED(rv)) {
   1184        printf("CrashReporter::SetExceptionHandler failed!\n");
   1185        return 1;
   1186      }
   1187      MOZ_ASSERT(CrashReporter::GetEnabled());
   1188    }
   1189 
   1190    if (argc > 1 && !strcmp(argv[1], "--greomni")) {
   1191      nsCOMPtr<nsIFile> greOmni;
   1192      XRE_GetFileFromPath(argv[2], getter_AddRefs(greOmni));
   1193      XRE_InitOmnijar(greOmni, greOmni);
   1194      argc -= 2;
   1195      argv += 2;
   1196    }
   1197 
   1198    rv = NS_InitXPCOM(nullptr, appDir, &dirprovider);
   1199    if (NS_FAILED(rv)) {
   1200      printf("NS_InitXPCOM failed!\n");
   1201      return 1;
   1202    }
   1203 
   1204    // Now that the profiler, directory services, and prefs have been
   1205    // initialized we can find the download directory, where the profiler can
   1206    // write profiles when user stops the profiler using POSIX signal handling.
   1207    profiler_lookup_async_signal_dump_directory();
   1208 
   1209    // xpc::ErrorReport::LogToConsoleWithStack needs this to print errors
   1210    // to stderr.
   1211    Preferences::SetBool("browser.dom.window.dump.enabled", true);
   1212    Preferences::SetBool("devtools.console.stdout.chrome", true);
   1213 
   1214    AutoJSAPI jsapi;
   1215    jsapi.Init();
   1216    cx = jsapi.cx();
   1217 
   1218    // Override the default XPConnect interrupt callback. We could store the
   1219    // old one and restore it before shutting down, but there's not really a
   1220    // reason to bother.
   1221    sScriptedInterruptCallback = new PersistentRootedValue;
   1222    sScriptedInterruptCallback->init(cx, UndefinedValue());
   1223 
   1224    JS_AddInterruptCallback(cx, XPCShellInterruptCallback);
   1225 
   1226    argc--;
   1227    argv++;
   1228 
   1229    nsCOMPtr<nsIPrincipal> systemprincipal;
   1230    // Fetch the system principal and store it away in a global, to use for
   1231    // script compilation in Load() and ProcessFile() (including interactive
   1232    // eval loop)
   1233    {
   1234      nsCOMPtr<nsIScriptSecurityManager> securityManager =
   1235          do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   1236      if (NS_SUCCEEDED(rv) && securityManager) {
   1237        rv = securityManager->GetSystemPrincipal(
   1238            getter_AddRefs(systemprincipal));
   1239        if (NS_FAILED(rv)) {
   1240          fprintf(gErrFile,
   1241                  "+++ Failed to obtain SystemPrincipal from "
   1242                  "ScriptSecurityManager service.\n");
   1243        } else {
   1244          // fetch the JS principals and stick in a global
   1245          gJSPrincipals = nsJSPrincipals::get(systemprincipal);
   1246          JS_HoldPrincipals(gJSPrincipals);
   1247        }
   1248      } else {
   1249        fprintf(gErrFile,
   1250                "+++ Failed to get ScriptSecurityManager service, running "
   1251                "without principals");
   1252      }
   1253    }
   1254 
   1255    const JSSecurityCallbacks* scb = JS_GetSecurityCallbacks(cx);
   1256    MOZ_ASSERT(
   1257        scb,
   1258        "We are assuming that nsScriptSecurityManager::Init() has been run");
   1259    shellSecurityCallbacks = *scb;
   1260    JS_SetSecurityCallbacks(cx, &shellSecurityCallbacks);
   1261 
   1262    auto systemGlobal = MakeRefPtr<SystemGlobal>();
   1263 
   1264    // Make the default XPCShell global use a fresh zone (rather than the
   1265    // System Zone) to improve cross-zone test coverage.
   1266    JS::RealmOptions options;
   1267    options.creationOptions().setNewCompartmentAndZone();
   1268    xpc::SetPrefableRealmOptions(options);
   1269 
   1270    // Even if we're building in a configuration where source is
   1271    // discarded, there's no reason to do that on XPCShell, and doing so
   1272    // might break various automation scripts.
   1273    options.behaviors().setDiscardSource(false);
   1274 
   1275    JS::Rooted<JSObject*> glob(cx);
   1276    rv = xpc::InitClassesWithNewWrappedGlobal(
   1277        cx, static_cast<nsIGlobalObject*>(systemGlobal), systemprincipal, 0,
   1278        options, &glob);
   1279    if (NS_FAILED(rv)) {
   1280      return 1;
   1281    }
   1282 
   1283    // Initialize e10s check on the main thread, if not already done
   1284    BrowserTabsRemoteAutostart();
   1285 #if defined(XP_WIN)
   1286    // Ensure that DLL Services are running
   1287    RefPtr<DllServices> dllSvc(DllServices::Get());
   1288    dllSvc->StartUntrustedModulesProcessor(true);
   1289    auto dllServicesDisable =
   1290        MakeScopeExit([&dllSvc]() { dllSvc->DisableFull(); });
   1291 
   1292 #  if defined(MOZ_SANDBOX)
   1293    // Required for sandboxed child processes.
   1294    if (aShellData->sandboxBrokerServices) {
   1295      SandboxBroker::Initialize(aShellData->sandboxBrokerServices, binDirPath);
   1296      SandboxBroker::GeckoDependentInitialize();
   1297    } else {
   1298      NS_WARNING(
   1299          "Failed to initialize broker services, sandboxed "
   1300          "processes will fail to start.");
   1301    }
   1302 #  endif  // defined(MOZ_SANDBOX)
   1303 
   1304    {
   1305      DebugOnly<bool> result = WindowsBCryptInitialization();
   1306      MOZ_ASSERT(result);
   1307    }
   1308 #endif  // defined(XP_WIN)
   1309 
   1310 #ifdef MOZ_CODE_COVERAGE
   1311    CodeCoverageHandler::Init();
   1312 #endif
   1313 
   1314    {
   1315      if (!glob) {
   1316        return 1;
   1317      }
   1318 
   1319      nsCOMPtr<nsIAppStartup> appStartup(components::AppStartup::Service());
   1320      if (!appStartup) {
   1321        return 1;
   1322      }
   1323      appStartup->DoneStartingUp();
   1324 
   1325      systemGlobal->SetGlobalObject(glob);
   1326 
   1327      JSAutoRealm ar(cx, glob);
   1328 
   1329      if (!JS_InitReflectParse(cx, glob)) {
   1330        return 1;
   1331      }
   1332 
   1333      if (!JS_DefineFunctions(cx, glob, glob_functions)) {
   1334        return 1;
   1335      }
   1336 
   1337      nsAutoString workingDirectory;
   1338      if (GetCurrentWorkingDirectory(workingDirectory)) {
   1339        gWorkingDirectory = &workingDirectory;
   1340      }
   1341 
   1342      JS_DefineProperty(cx, glob, "__LOCATION__", GetLocationProperty, nullptr,
   1343                        0);
   1344 
   1345      {
   1346 #ifdef FUZZING_INTERFACES
   1347        if (fuzzHaveModule) {
   1348          // argv[0] was removed previously, but libFuzzer expects it
   1349          argc++;
   1350          argv--;
   1351 
   1352          result = FuzzXPCRuntimeStart(&jsapi, &argc, &argv, aShellData);
   1353        } else {
   1354 #endif
   1355          // We are almost certainly going to run script here, so we need an
   1356          // AutoEntryScript. This is Gecko-specific and not in any spec.
   1357          AutoEntryScript aes(systemGlobal, "xpcshell argument processing");
   1358 
   1359          // If an exception is thrown, we'll set our return code
   1360          // appropriately, and then let the AutoEntryScript destructor report
   1361          // the error to the console.
   1362          if (!ProcessArgs(aes, argv, argc, &dirprovider)) {
   1363            if (gExitCode) {
   1364              result = gExitCode;
   1365            } else if (gQuitting) {
   1366              result = 0;
   1367            } else {
   1368              result = EXITCODE_RUNTIME_ERROR;
   1369            }
   1370          }
   1371 #ifdef FUZZING_INTERFACES
   1372        }
   1373 #endif
   1374      }
   1375 
   1376      // Signal that we're now shutting down.
   1377      nsCOMPtr<nsIObserver> obs = do_QueryInterface(appStartup);
   1378      if (obs) {
   1379        obs->Observe(nullptr, "quit-application-forced", nullptr);
   1380      }
   1381 
   1382 #ifdef MOZ_WIDGET_ANDROID
   1383      MOZ_ASSERT(jni::IsAvailable());
   1384      java::GeckoThread::CheckAndSetState(java::GeckoThread::State::RUNNING(),
   1385                                          java::GeckoThread::State::EXITING());
   1386 #endif
   1387 
   1388      JS_DropPrincipals(cx, gJSPrincipals);
   1389      JS_SetAllNonReservedSlotsToUndefined(glob);
   1390      JS::RootedObject lexicalEnv(cx, JS_GlobalLexicalEnvironment(glob));
   1391      JS_SetAllNonReservedSlotsToUndefined(lexicalEnv);
   1392      JS_GC(cx);
   1393    }
   1394    JS_GC(cx);
   1395 
   1396    dirprovider.ClearGREDirs();
   1397    dirprovider.ClearAppDir();
   1398    dirprovider.ClearAppFile();
   1399  }  // this scopes the nsCOMPtrs
   1400 
   1401  if (!XRE_ShutdownTestShell()) {
   1402    NS_ERROR("problem shutting down testshell");
   1403  }
   1404 
   1405  // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
   1406  rv = NS_ShutdownXPCOM(nullptr);
   1407  MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
   1408 
   1409  // Shut down the crashreporter service to prevent leaking some strings it
   1410  // holds.
   1411  if (CrashReporter::GetEnabled()) {
   1412    CrashReporter::UnsetExceptionHandler();
   1413  }
   1414 
   1415  // This must precede NS_LogTerm(), otherwise xpcshell return non-zero
   1416  // during some tests, which causes failures.
   1417  profiler_shutdown();
   1418 
   1419  NS_LogTerm();
   1420 
   1421  XRE_DeinitCommandLine();
   1422 
   1423  return result;
   1424 }
   1425 
   1426 void XPCShellDirProvider::SetGREDirs(nsIFile* greDir) {
   1427  mGREDir = greDir;
   1428  mGREDir->Clone(getter_AddRefs(mGREBinDir));
   1429 
   1430 #ifdef XP_MACOSX
   1431  nsAutoCString leafName;
   1432  mGREDir->GetNativeLeafName(leafName);
   1433  if (leafName.EqualsLiteral("Resources")) {
   1434    mGREBinDir->SetNativeLeafName("MacOS"_ns);
   1435  }
   1436 #endif
   1437 }
   1438 
   1439 void XPCShellDirProvider::SetAppFile(nsIFile* appFile) { mAppFile = appFile; }
   1440 
   1441 void XPCShellDirProvider::SetAppDir(nsIFile* appDir) { mAppDir = appDir; }
   1442 
   1443 NS_IMETHODIMP_(MozExternalRefCountType)
   1444 XPCShellDirProvider::AddRef() { return 2; }
   1445 
   1446 NS_IMETHODIMP_(MozExternalRefCountType)
   1447 XPCShellDirProvider::Release() { return 1; }
   1448 
   1449 NS_IMPL_QUERY_INTERFACE(XPCShellDirProvider, nsIDirectoryServiceProvider,
   1450                        nsIDirectoryServiceProvider2)
   1451 
   1452 NS_IMETHODIMP
   1453 XPCShellDirProvider::GetFile(const char* prop, bool* persistent,
   1454                             nsIFile** result) {
   1455  if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
   1456    *persistent = true;
   1457    return mGREDir->Clone(result);
   1458  } else if (mGREBinDir && !strcmp(prop, NS_GRE_BIN_DIR)) {
   1459    *persistent = true;
   1460    return mGREBinDir->Clone(result);
   1461  } else if (mAppFile && !strcmp(prop, XRE_EXECUTABLE_FILE)) {
   1462    *persistent = true;
   1463    return mAppFile->Clone(result);
   1464  } else if (mGREDir && !strcmp(prop, NS_APP_PREF_DEFAULTS_50_DIR)) {
   1465    nsCOMPtr<nsIFile> file;
   1466    *persistent = true;
   1467    if (NS_FAILED(mGREDir->Clone(getter_AddRefs(file))) ||
   1468        NS_FAILED(file->AppendNative("defaults"_ns)) ||
   1469        NS_FAILED(file->AppendNative("pref"_ns)))
   1470      return NS_ERROR_FAILURE;
   1471    file.forget(result);
   1472    return NS_OK;
   1473  }
   1474 
   1475  return NS_ERROR_FAILURE;
   1476 }
   1477 
   1478 NS_IMETHODIMP
   1479 XPCShellDirProvider::GetFiles(const char* prop, nsISimpleEnumerator** result) {
   1480  if (mGREDir && !strcmp(prop, "ChromeML")) {
   1481    nsCOMArray<nsIFile> dirs;
   1482 
   1483    nsCOMPtr<nsIFile> file;
   1484    mGREDir->Clone(getter_AddRefs(file));
   1485    file->AppendNative("chrome"_ns);
   1486    dirs.AppendObject(file);
   1487 
   1488    nsresult rv =
   1489        NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(file));
   1490    if (NS_SUCCEEDED(rv)) {
   1491      dirs.AppendObject(file);
   1492    }
   1493 
   1494    return NS_NewArrayEnumerator(result, dirs, NS_GET_IID(nsIFile));
   1495  } else if (!strcmp(prop, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
   1496    nsCOMArray<nsIFile> dirs;
   1497    nsCOMPtr<nsIFile> appDir;
   1498    bool exists;
   1499    if (mAppDir && NS_SUCCEEDED(mAppDir->Clone(getter_AddRefs(appDir))) &&
   1500        NS_SUCCEEDED(appDir->AppendNative("defaults"_ns)) &&
   1501        NS_SUCCEEDED(appDir->AppendNative("preferences"_ns)) &&
   1502        NS_SUCCEEDED(appDir->Exists(&exists)) && exists) {
   1503      dirs.AppendObject(appDir);
   1504      return NS_NewArrayEnumerator(result, dirs, NS_GET_IID(nsIFile));
   1505    }
   1506    return NS_ERROR_FAILURE;
   1507  }
   1508  return NS_ERROR_FAILURE;
   1509 }