/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef ANDROID # include <android/log.h> # include "XREShellData.h" #endif
#ifdef XP_WIN # include "mozilla/mscom/ProcessRuntime.h" # include "mozilla/ScopeExit.h" # include "mozilla/widget/AudioSession.h" # include "mozilla/WinDllServices.h" # include "mozilla/WindowsBCryptInitialization.h" # include <windows.h> # ifdefined(MOZ_SANDBOX) # include "XREShellData.h" # include "sandboxBroker.h" # endif #endif
#ifdef MOZ_CODE_COVERAGE # include "mozilla/CodeCoverageHandler.h" #endif
// all this crap is needed to do the interactive shell stuff #include <stdlib.h> #include <errno.h> #ifdef HAVE_IO_H # include <io.h> /* for isatty() */ #endif #ifdef HAVE_UNISTD_H # include <unistd.h> /* for isatty() */ #endif
#ifdef ENABLE_TESTS # include "xpctest_private.h" #endif
// Fuzzing support for XPC runtime fuzzing #ifdef FUZZING_INTERFACES # include "xpcrtfuzzing/xpcrtfuzzing.h" # include "XREShellData.h"
MOZ_RUNINIT staticbool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG");
MOZ_RUNINIT staticbool fuzzHaveModule = !!getenv("FUZZER"); #endif// FUZZING_INTERFACES
usingnamespace mozilla; usingnamespace JS; using mozilla::dom::AutoEntryScript; using mozilla::dom::AutoJSAPI;
class XPCShellDirProvider : public nsIDirectoryServiceProvider2 { public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDIRECTORYSERVICEPROVIDER
NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
staticbool GetLocationProperty(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject()) {
JS_ReportErrorASCII(cx, "Unexpected this value for GetLocationProperty"); returnfalse;
} #if !defined(XP_WIN) && !defined(XP_UNIX) // XXX: your platform should really implement this returnfalse; #else
JS::AutoFilename filename; if (JS::DescribeScriptedCaller(&filename, cx) && filename.get()) {
NS_ConvertUTF8toUTF16 filenameString(filename.get());
# ifdefined(XP_WIN) // replace forward slashes with backslashes, // since nsLocalFileWin chokes on them
char16_t* start = filenameString.BeginWriting();
char16_t* end = filenameString.EndWriting();
while (start != end) { if (*start == L'/') {
*start = L'\\';
}
start++;
} # endif
if (!location && gWorkingDirectory) { // could be a relative path, try appending it to the cwd // and then normalize
nsAutoString absolutePath(*gWorkingDirectory);
absolutePath.Append(filenameString);
// While 4096 might be quite arbitrary, this is something to be fixed in // bug 105707. It is also the same limit as in ProcessFile. char buf[4096];
RootedString str(cx);
/* If a prompt was specified, construct the string */ if (args.length() > 0) {
str = JS::ToString(cx, args[0]); if (!str) { returnfalse;
}
} else {
str = JS_GetEmptyString(cx);
}
/* Get a line from the infile */
JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str); if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.get())) { returnfalse;
}
/* Strip newline character added by GetLine() */ unsignedint buflen = strlen(buf); if (buflen == 0) { if (feof(gInFile)) {
args.rval().setNull(); returntrue;
}
} elseif (buf[buflen - 1] == '\n') {
--buflen;
}
/* Turn buf into a JSString */
str = JS_NewStringCopyN(cx, buf, buflen); if (!str) { returnfalse;
}
#ifdef FUZZING_INTERFACES if (fuzzHaveModule && !fuzzDoDebug) { // When fuzzing and not debugging, suppress any print() output, // as it slows down fuzzing and makes libFuzzer's output hard // to read. returntrue;
} #endif// FUZZING_INTERFACES
RootedString str(cx);
nsAutoCString utf8output;
for (unsigned i = 0; i < args.length(); i++) {
str = ToString(cx, args[i]); if (!str) { returnfalse;
}
JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str); if (!utf8str) { returnfalse;
}
JS::RootedObject thisObject(cx); if (!args.computeThis(cx, &thisObject)) { returnfalse;
} if (!JS_IsGlobalObject(thisObject)) {
JS_ReportErrorASCII(cx, "Trying to load() into a non-global object"); returnfalse;
}
RootedString str(cx); for (unsigned i = 0; i < args.length(); i++) {
str = ToString(cx, args[i]); if (!str) { returnfalse;
}
JS::UniqueChars filename = JS_EncodeStringToUTF8(cx, str); if (!filename) { returnfalse;
}
JS::CompileOptions options(cx);
options.setIsRunOnce(true).setSkipFilenameValidation(true);
JS::Rooted<JSScript*> script(
cx, JS::CompileUtf8Path(cx, options, filename.get())); if (!script) { returnfalse;
}
if (!compileOnly) { if (!JS_ExecuteScript(cx, script)) { returnfalse;
}
}
}
args.rval().setUndefined(); returntrue;
}
if (args.length() == 0) {
JS_ReportErrorASCII(cx, "Function takes at least one argument!"); returnfalse;
}
RootedString str(cx, ToString(cx, args[0])); if (!str) {
JS_ReportErrorASCII(cx, "Could not convert argument 1 to string!"); returnfalse;
}
if (args.get(1).isObject() && !JS_ObjectIsFunction(&args[1].toObject())) {
JS_ReportErrorASCII(cx, "Could not convert argument 2 to function!"); returnfalse;
}
// Sanity-check args.
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() != 1) {
JS_ReportErrorASCII(cx, "Wrong number of arguments"); returnfalse;
}
// Allow callers to remove the interrupt callback by passing undefined. if (args[0].isUndefined()) {
*sScriptedInterruptCallback = UndefinedValue(); returntrue;
}
// Otherwise, we should have a function object. if (!args[0].isObject() || !js::IsFunctionObject(&args[0].toObject())) {
JS_ReportErrorASCII(cx, "Argument must be a function"); returnfalse;
}
// This mimics mozilla::SpinEventLoopUntil but instead of spinning the // event loop we sleep, to make sure we don't run script.
xpc::AutoScriptActivity asa(false);
PR_Sleep(PR_SecondsToInterval(args[0].toInt32()));
args.rval().setUndefined(); returntrue;
}
staticbool RegisterAppManifest(JSContext* cx, unsigned argc, Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() != 1) {
JS_ReportErrorASCII(cx, "Wrong number of arguments"); returnfalse;
} if (!args[0].isObject()) {
JS_ReportErrorASCII(cx, "Expected object as argument 1 to registerAppManifest"); returnfalse;
}
#ifdef ANDROID staticbool ChangeTestShellDir(JSContext* cx, unsigned argc, Value* vp) { // This method should only be used by testing/xpcshell/head.js to change to // the correct directory on Android Remote XPCShell tests. // // TODO: Bug 1801725 - Find a more ergonomic way to do this than exposing // identical methods in XPCShellEnvironment and XPCShellImpl to chdir on // android for Remote XPCShell tests on Android.
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
JS_ReportErrorASCII(cx, "changeTestShellDir() takes one argument"); returnfalse;
}
nsAutoJSCString path; if (!path.init(cx, args[0])) {
JS_ReportErrorASCII(
cx, "changeTestShellDir(): could not convert argument 1 to string"); returnfalse;
}
if (chdir(path.get())) {
JS_ReportErrorASCII(cx, "changeTestShellDir(): could not change directory"); returnfalse;
}
if (forceTTY) {
file = stdin;
} elseif (!isatty(fileno(file))) { /* * It's not interactive - just execute it. * * Support the UNIX #! shell hack; gobble the first line if it starts * with '#'.
*/ int ch = fgetc(file); if (ch == '#') { while ((ch = fgetc(file)) != EOF) { if (ch == '\n' || ch == '\r') { break;
}
}
}
ungetc(ch, file);
JS::UniqueChars filenameUtf8 = JS::EncodeNarrowToUtf8(jsapi.cx(), filename); if (!filenameUtf8) { returnfalse;
}
/* It's an interactive filehandle; drop into read-eval-print loop. */ int lineno = 1; bool hitEOF = false; do { char buffer[4096]; char* bufp = buffer;
*bufp = '\0';
/* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line.
*/ int startline = lineno; do { if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
hitEOF = true; break;
}
bufp += strlen(bufp);
lineno++;
} while (
!JS_Utf8BufferIsCompilableUnit(cx, global, buffer, strlen(buffer)));
if (!ProcessUtf8Line(jsapi, buffer, startline)) {
jsapi.ReportException();
}
} while (!hitEOF && !gQuitting);
if (forceTTY || !filename || strcmp(filename, "-") == 0) {
file = stdin;
} else {
file = fopen(filename, "r"); if (!file) { /* * Use Latin1 variant here because the encoding of the return value * of strerror function can be non-UTF-8.
*/
JS_ReportErrorNumberLatin1(jsapi.cx(), my_GetErrorMessage, nullptr,
JSSMSG_CANT_OPEN, filename, strerror(errno));
gExitCode = EXITCODE_FILE_NOT_FOUND; returnfalse;
}
}
bool ok = ProcessFile(jsapi, filename, file, forceTTY); if (file != stdin) {
fclose(file);
} return ok;
}
/* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. * First of all, find out the first argument position which will be passed * as a script file to be executed.
*/ for (rootPosition = 0; rootPosition < argc; rootPosition++) { if (argv[rootPosition][0] != '-' || argv[rootPosition][1] == '\0') {
++rootPosition; break;
}
bool isPairedFlag =
argv[rootPosition][0] != '\0' &&
(argv[rootPosition][1] == 'v' || argv[rootPosition][1] == 'f' ||
argv[rootPosition][1] == 'e'); if (isPairedFlag && rootPosition < argc - 1) {
++rootPosition; // Skip over the 'foo' portion of |-v foo|, |-f foo|, or // |-e foo|.
}
}
/* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f <file> arguments.
*/
argsObj = JS::NewArrayObject(cx, 0); if (!argsObj) { return1;
} if (!JS_DefineProperty(cx, global, "arguments", argsObj, 0)) { return1;
}
staticbool GetCurrentWorkingDirectory(nsAString& workingDirectory) { #if !defined(XP_WIN) && !defined(XP_UNIX) // XXX: your platform should really implement this returnfalse; #elif XP_WIN
DWORD requiredLength = GetCurrentDirectoryW(0, nullptr);
workingDirectory.SetLength(requiredLength);
GetCurrentDirectoryW(workingDirectory.Length(),
(LPWSTR)workingDirectory.BeginWriting()); // we got a trailing null there
workingDirectory.SetLength(requiredLength);
workingDirectory.Replace(workingDirectory.Length() - 1, 1, L'\\'); #elifdefined(XP_UNIX)
nsAutoCString cwd; // 1024 is just a guess at a sane starting value
size_t bufsize = 1024; char* result = nullptr; while (result == nullptr) {
cwd.SetLength(bufsize);
result = getcwd(cwd.BeginWriting(), cwd.Length()); if (!result) { if (errno != ERANGE) { returnfalse;
} // need to make the buffer bigger
bufsize *= 2;
}
} // size back down to the actual string length
cwd.SetLength(strlen(result) + 1);
cwd.Replace(cwd.Length() - 1, 1, '/');
CopyUTF8toUTF16(cwd, workingDirectory); #endif returntrue;
}
// This guard ensures that all threads that attempt to register themselves // with the IOInterposer will be properly tracked.
mozilla::AutoIOInterposer ioInterposerGuard;
ioInterposerGuard.Init();
if (PR_GetEnv("MOZ_CHAOSMODE")) {
ChaosFeature feature = ChaosFeature::Any; long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16); if (featureInt) { // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
feature = static_cast<ChaosFeature>(featureInt);
}
ChaosMode::SetChaosFeature(feature);
}
if (ChaosMode::isActive(ChaosFeature::Any)) {
printf_stderr( "*** You are running in chaos test mode. See ChaosMode.h. ***\n");
}
#ifdef XP_WIN // Some COM settings are global to the process and must be set before any non- // trivial COM is run in the application. Since these settings may affect // stability, we should instantiate COM ASAP so that we can ensure that these // global settings are configured before anything can interfere.
mscom::ProcessRuntime mscom;
#ifdefined(XP_WIN) && defined(MOZ_SANDBOX) // We need the binary directory to initialize the windows sandbox.
MOZ_ALWAYS_SUCCEEDS(appDir->GetPath(binDirPath)); #endif
dirprovider.SetAppFile(appFile);
nsCOMPtr<nsIFile> greDir; if (argc > 1 && !strcmp(argv[1], "-g")) { if (argc < 3) { return usage();
}
rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(greDir)); if (NS_FAILED(rv)) {
printf("Couldn't use given GRE dir.\n"); return1;
}
dirprovider.SetGREDirs(greDir);
argc -= 2;
argv += 2;
} else { #ifdef XP_MACOSX // On OSX, the GreD needs to point to Contents/Resources in the .app // bundle. Libraries will be loaded at a relative path to GreD, i.e. // ../MacOS.
nsCOMPtr<nsIFile> tmpDir;
XRE_GetFileFromPath(argv[0], getter_AddRefs(greDir));
greDir->GetParent(getter_AddRefs(tmpDir));
tmpDir->Clone(getter_AddRefs(greDir));
tmpDir->SetNativeLeafName("Resources"_ns); bool dirExists = false;
tmpDir->Exists(&dirExists); if (dirExists) {
greDir = tmpDir.forget();
}
dirprovider.SetGREDirs(greDir); #else
nsAutoString workingDir; if (!GetCurrentWorkingDirectory(workingDir)) {
printf("GetCurrentWorkingDirectory failed.\n"); return1;
}
rv = NS_NewLocalFile(workingDir, getter_AddRefs(greDir)); if (NS_FAILED(rv)) {
printf("NS_NewLocalFile failed.\n"); return1;
} #endif
}
if (argc > 1 && !strcmp(argv[1], "-a")) { if (argc < 3) { return usage();
}
nsCOMPtr<nsIFile> dir;
rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(dir)); if (NS_SUCCEEDED(rv)) {
appDir = dir;
dirprovider.SetAppDir(appDir);
} if (NS_FAILED(rv)) {
printf("Couldn't use given appdir.\n"); return1;
}
argc -= 2;
argv += 2;
}
while (argc > 1 && !strcmp(argv[1], "-r")) { if (argc < 3) { return usage();
}
nsCOMPtr<nsIFile> lf;
rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(lf)); if (NS_FAILED(rv)) {
printf("Couldn't get manifest file.\n"); return1;
}
XRE_AddManifestLocation(NS_APP_LOCATION, lf);
argc -= 2;
argv += 2;
}
constchar* val = getenv("MOZ_CRASHREPORTER"); if (val && *val && !CrashReporter::IsDummy()) {
rv = CrashReporter::SetExceptionHandler(greDir, true); if (NS_FAILED(rv)) {
printf("CrashReporter::SetExceptionHandler failed!\n"); return1;
}
MOZ_ASSERT(CrashReporter::GetEnabled());
}
// xpc::ErrorReport::LogToConsoleWithStack needs this to print errors // to stderr.
Preferences::SetBool("browser.dom.window.dump.enabled", true);
Preferences::SetBool("devtools.console.stdout.chrome", true);
AutoJSAPI jsapi;
jsapi.Init();
cx = jsapi.cx();
// Override the default XPConnect interrupt callback. We could store the // old one and restore it before shutting down, but there's not really a // reason to bother.
sScriptedInterruptCallback = new PersistentRootedValue;
sScriptedInterruptCallback->init(cx, UndefinedValue());
nsCOMPtr<nsIPrincipal> systemprincipal; // Fetch the system principal and store it away in a global, to use for // script compilation in Load() and ProcessFile() (including interactive // eval loop)
{
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && securityManager) {
rv = securityManager->GetSystemPrincipal(
getter_AddRefs(systemprincipal)); if (NS_FAILED(rv)) {
fprintf(gErrFile, "+++ Failed to obtain SystemPrincipal from " "ScriptSecurityManager service.\n");
} else { // fetch the JS principals and stick in a global
gJSPrincipals = nsJSPrincipals::get(systemprincipal);
JS_HoldPrincipals(gJSPrincipals);
}
} else {
fprintf(gErrFile, "+++ Failed to get ScriptSecurityManager service, running " "without principals");
}
}
const JSSecurityCallbacks* scb = JS_GetSecurityCallbacks(cx);
MOZ_ASSERT(
scb, "We are assuming that nsScriptSecurityManager::Init() has been run");
shellSecurityCallbacks = *scb;
JS_SetSecurityCallbacks(cx, &shellSecurityCallbacks);
auto systemGlobal = MakeRefPtr<SystemGlobal>();
// Make the default XPCShell global use a fresh zone (rather than the // System Zone) to improve cross-zone test coverage.
JS::RealmOptions options;
options.creationOptions().setNewCompartmentAndZone();
xpc::SetPrefableRealmOptions(options);
// Even if we're building in a configuration where source is // discarded, there's no reason to do that on XPCShell, and doing so // might break various automation scripts.
options.behaviors().setDiscardSource(false);
// Initialize e10s check on the main thread, if not already done
BrowserTabsRemoteAutostart(); #ifdefined(XP_WIN) // Plugin may require audio session if installed plugin can initialize // asynchronized.
AutoAudioSession audioSession;
// Ensure that DLL Services are running
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->StartUntrustedModulesProcessor(true); auto dllServicesDisable =
MakeScopeExit([&dllSvc]() { dllSvc->DisableFull(); });
# ifdefined(MOZ_SANDBOX) // Required for sandboxed child processes. if (aShellData->sandboxBrokerServices) {
SandboxBroker::Initialize(aShellData->sandboxBrokerServices, binDirPath);
SandboxBroker::GeckoDependentInitialize();
} else {
NS_WARNING( "Failed to initialize broker services, sandboxed " "processes will fail to start.");
} # endif // defined(MOZ_SANDBOX)
{
DebugOnly<bool> result = WindowsBCryptInitialization();
MOZ_ASSERT(result);
} #endif// defined(XP_WIN)
{ #ifdef FUZZING_INTERFACES if (fuzzHaveModule) { // argv[0] was removed previously, but libFuzzer expects it
argc++;
argv--;
result = FuzzXPCRuntimeStart(&jsapi, &argc, &argv, aShellData);
} else { #endif // We are almost certainly going to run script here, so we need an // AutoEntryScript. This is Gecko-specific and not in any spec.
AutoEntryScript aes(systemGlobal, "xpcshell argument processing");
// If an exception is thrown, we'll set our return code // appropriately, and then let the AutoEntryScript destructor report // the error to the console. if (!ProcessArgs(aes, argv, argc, &dirprovider)) { if (gExitCode) {
result = gExitCode;
} elseif (gQuitting) {
result = 0;
} else {
result = EXITCODE_RUNTIME_ERROR;
}
} #ifdef FUZZING_INTERFACES
} #endif
}
// Signal that we're now shutting down.
nsCOMPtr<nsIObserver> obs = do_QueryInterface(appStartup); if (obs) {
obs->Observe(nullptr, "quit-application-forced", nullptr);
}
dirprovider.ClearGREDirs();
dirprovider.ClearAppDir();
dirprovider.ClearAppFile();
} // this scopes the nsCOMPtrs
if (!XRE_ShutdownTestShell()) {
NS_ERROR("problem shutting down testshell");
}
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
rv = NS_ShutdownXPCOM(nullptr);
MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
// Shut down the crashreporter service to prevent leaking some strings it // holds. if (CrashReporter::GetEnabled()) {
CrashReporter::UnsetExceptionHandler();
}
// This must precede NS_LogTerm(), otherwise xpcshell return non-zero // during some tests, which causes failures.
profiler_shutdown();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.