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 }