Bug 1567341 - Allow geckoview.test to run xpcshell. r=esawin

This commit adds a new command line option |-xpcshell| that, when passed, will
run an xpcshell instead of launching a full Gecko instance.

This command line option is restricted to org.mozilla.geckoview.test for now,
as it's really hard to use and not really a usecase outside mozilla. We can
revisit this if there's interest.

Differential Revision: https://phabricator.services.mozilla.com/D106211
This commit is contained in:
Agi Sferro 2021-03-24 20:20:01 +00:00
parent af745a8adf
commit 5256b36255
7 changed files with 101 additions and 37 deletions

View File

@ -154,6 +154,8 @@ public class GeckoThread extends Thread {
public Map<String, Object> prefs;
public String userSerialNumber;
public boolean xpcshell;
public String outFilePath;
public int prefsFd;
public int prefMapFd;
public int ipcFd;
@ -274,17 +276,20 @@ public class GeckoThread extends Thread {
// argv[0] is the program name, which for us is the package name.
args.add(context.getPackageName());
args.add("-greomni");
args.add(context.getPackageResourcePath());
final GeckoProfile profile = getProfile();
if (profile.isCustomProfile()) {
args.add("-profile");
args.add(profile.getDir().getAbsolutePath());
} else {
profile.getDir(); // Make sure the profile dir exists.
args.add("-P");
args.add(profile.getName());
if (!mInitInfo.xpcshell) {
args.add("-greomni");
args.add(context.getPackageResourcePath());
final GeckoProfile profile = getProfile();
if (profile.isCustomProfile()) {
args.add("-profile");
args.add(profile.getDir().getAbsolutePath());
} else {
profile.getDir(); // Make sure the profile dir exists.
args.add("-P");
args.add(profile.getName());
}
}
if (mInitInfo.args != null) {
@ -435,7 +440,8 @@ public class GeckoThread extends Thread {
final boolean isChildProcess = isChildProcess();
GeckoLoader.setupGeckoEnvironment(context, isChildProcess,
context.getFilesDir().getPath(), env, mInitInfo.prefs);
context.getFilesDir().getPath(), env, mInitInfo.prefs,
mInitInfo.xpcshell);
initGeckoEnvironment();
@ -465,7 +471,9 @@ public class GeckoThread extends Thread {
mInitInfo.extras.getInt(EXTRA_PREF_MAP_FD, -1),
mInitInfo.extras.getInt(EXTRA_IPC_FD, -1),
mInitInfo.extras.getInt(EXTRA_CRASH_FD, -1),
mInitInfo.extras.getInt(EXTRA_CRASH_ANNOTATION_FD, -1));
mInitInfo.extras.getInt(EXTRA_CRASH_ANNOTATION_FD, -1),
isChildProcess ? false : mInitInfo.xpcshell,
isChildProcess ? null : mInitInfo.outFilePath);
// And... we're done.
final boolean restarting = isState(State.RESTARTING);

View File

@ -118,7 +118,8 @@ public final class GeckoLoader {
final boolean isChildProcess,
final String profilePath,
final Collection<String> env,
final Map<String, Object> prefs) {
final Map<String, Object> prefs,
final boolean xpcshell) {
for (final String e : env) {
putenv(e);
}
@ -156,12 +157,15 @@ public final class GeckoLoader {
setupInitialPrefs(prefs);
}
// setup the tmp path
final File f = getTmpDir(context);
if (!f.exists()) {
f.mkdirs();
// Xpcshell tests set up their own temp directory
if (!xpcshell) {
// setup the tmp path
final File f = getTmpDir(context);
if (!f.exists()) {
f.mkdirs();
}
putenv("TMPDIR=" + f.getPath());
}
putenv("TMPDIR=" + f.getPath());
putenv("LANG=" + Locale.getDefault().toString());
@ -496,7 +500,7 @@ public final class GeckoLoader {
private static native void putenv(String map);
// These methods are implemented in mozglue/android/APKOpen.cpp
public static native void nativeRun(String[] args, int prefsFd, int prefMapFd, int ipcFd, int crashFd, int crashAnnotationFd);
public static native void nativeRun(String[] args, int prefsFd, int prefMapFd, int ipcFd, int crashFd, int crashAnnotationFd, boolean xpcshell, String outFilePath);
private static native void loadGeckoLibsNative();
private static native void loadSQLiteLibsNative();
private static native void loadNSSLibsNative();

View File

@ -49,6 +49,7 @@ import org.mozilla.gecko.util.ThreadUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -289,6 +290,27 @@ public final class GeckoRuntime implements Parcelable {
return null;
}
private void setArguments(final Context context, final GeckoThread.InitInfo initInfo,
final String[] arguments) {
final List<String> result = new ArrayList<>(arguments.length);
for (final String argument : arguments) {
if ("-xpcshell".equals(argument)) {
// Only debug builds of the test app can run an xpcshell
if (!BuildConfig.DEBUG
|| !"org.mozilla.geckoview.test".equals(
context.getApplicationContext().getPackageName())) {
throw new IllegalArgumentException("Only the test app can run -xpcshell.");
}
initInfo.xpcshell = true;
} else {
result.add(argument);
}
}
initInfo.args = result.toArray(new String[]{});
}
/* package */ boolean init(final @NonNull Context context, final @NonNull GeckoRuntimeSettings settings) {
if (DEBUG) {
Log.d(LOGTAG, "init");
@ -329,7 +351,10 @@ public final class GeckoRuntime implements Parcelable {
GeckoFontScaleListener.getInstance().attachToContext(context, settings);
final GeckoThread.InitInfo info = new GeckoThread.InitInfo();
info.args = settings.getArguments();
setArguments(context, info, settings.getArguments());
if (info.xpcshell) {
info.outFilePath = settings.getExtras().getString("out_file");
}
info.extras = settings.getExtras();
info.flags = flags;

View File

@ -362,11 +362,10 @@ static void FreeArgv(char** argv, int argc) {
}
extern "C" APKOPEN_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv* jenv, jclass jc,
jobjectArray jargs,
int prefsFd, int prefMapFd,
int ipcFd, int crashFd,
int crashAnnotationFd) {
Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(
JNIEnv* jenv, jclass jc, jobjectArray jargs, int prefsFd, int prefMapFd,
int ipcFd, int crashFd, int crashAnnotationFd, bool xpcshell,
jstring outFilePath) {
EnsureBaseProfilerInitialized();
int argc = 0;
@ -381,7 +380,15 @@ Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv* jenv, jclass jc,
#ifdef MOZ_LINKER
ElfLoader::Singleton.ExpectShutdown(false);
#endif
gBootstrap->GeckoStart(jenv, argv, argc, sAppData);
const char* outFilePathRaw = nullptr;
if (xpcshell) {
MOZ_ASSERT(outFilePath);
outFilePathRaw = jenv->GetStringUTFChars(outFilePath, nullptr);
}
gBootstrap->GeckoStart(jenv, argv, argc, sAppData, xpcshell, outFilePathRaw);
if (outFilePathRaw) {
jenv->ReleaseStringUTFChars(outFilePath, outFilePathRaw);
}
#ifdef MOZ_LINKER
ElfLoader::Singleton.ExpectShutdown(true);
#endif

View File

@ -73,8 +73,9 @@ class BootstrapImpl final : public Bootstrap {
#ifdef MOZ_WIDGET_ANDROID
virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc,
const StaticXREAppData& aAppData) override {
::GeckoStart(aEnv, argv, argc, aAppData);
const StaticXREAppData& aAppData,
bool xpcshell, const char* outFilePath) override {
::GeckoStart(aEnv, argv, argc, aAppData, xpcshell, outFilePath);
}
virtual void XRE_SetAndroidChildFds(

View File

@ -28,7 +28,8 @@ struct StaticXREAppData;
}
extern "C" NS_EXPORT void GeckoStart(JNIEnv* aEnv, char** argv, int argc,
const mozilla::StaticXREAppData& aAppData);
const mozilla::StaticXREAppData& aAppData,
bool xpcshell, const char* outFilePath);
#endif
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
@ -120,7 +121,8 @@ class Bootstrap {
#ifdef MOZ_WIDGET_ANDROID
virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc,
const StaticXREAppData& aAppData) = 0;
const StaticXREAppData& aAppData, bool xpcshell,
const char* outFilePath) = 0;
virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv,
const XRE_AndroidChildFds& fds) = 0;

View File

@ -17,13 +17,15 @@
#include "nsAppRunner.h"
#include "nsExceptionHandler.h"
#include "mozilla/Bootstrap.h"
#include "XREShellData.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, MOZ_APP_NAME, args)
using namespace mozilla;
extern "C" NS_EXPORT void GeckoStart(JNIEnv* env, char** argv, int argc,
const StaticXREAppData& aAppData) {
const StaticXREAppData& aAppData,
bool xpcshell, const char* outFilePath) {
mozilla::jni::SetGeckoThreadEnv(env);
if (!argv) {
@ -31,11 +33,26 @@ extern "C" NS_EXPORT void GeckoStart(JNIEnv* env, char** argv, int argc,
return;
}
BootstrapConfig config;
config.appData = &aAppData;
config.appDataPath = nullptr;
if (xpcshell) {
XREShellData shellData;
FILE* outFile = fopen(outFilePath, "w");
if (!outFile) {
LOG("XRE_XPCShellMain cannot open %s", outFilePath);
return;
}
// We redirect both stdout and stderr to the same file, to conform with
// what runxpcshell.py does on Desktop.
shellData.outFile = outFile;
shellData.errFile = outFile;
int result = XRE_XPCShellMain(argc, argv, nullptr, &shellData);
fclose(shellData.outFile);
if (result) LOG("XRE_XPCShellMain returned %d", result);
} else {
BootstrapConfig config;
config.appData = &aAppData;
config.appDataPath = nullptr;
int result = XRE_main(argc, argv, config);
if (result) LOG("XRE_main returned %d", result);
int result = XRE_main(argc, argv, config);
if (result) LOG("XRE_main returned %d", result);
}
}