Bug 590057: Add support for loading plugins of a different architecture than the host browser on Mac OS X. r=cjones a=blocking-b7

This commit is contained in:
Josh Aas 2010-09-15 23:09:19 -07:00
Родитель 1fa97831bc
Коммит 5c84bcf9f6
7 изменённых файлов: 276 добавлений и 46 удалений

Просмотреть файл

@ -40,6 +40,8 @@
#include "mozilla/plugins/PluginProcessParent.h"
#include "base/string_util.h"
#include "base/process_util.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/plugins/PluginMessageUtils.h"
@ -49,6 +51,7 @@ using std::string;
using mozilla::ipc::BrowserProcessSubThread;
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::plugins::PluginProcessParent;
using base::ProcessArchitecture;
template<>
struct RunnableMethodTraits<PluginProcessParent>
@ -70,9 +73,42 @@ PluginProcessParent::~PluginProcessParent()
bool
PluginProcessParent::Launch(PRInt32 timeoutMs)
{
ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
uint32 containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
uint32 pluginLibArchitectures = currentArchitecture;
#ifdef XP_MACOSX
nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
if (NS_FAILED(rv)) {
// If the call failed just assume that we want the current architecture.
pluginLibArchitectures = currentArchitecture;
}
#endif
ProcessArchitecture selectedArchitecture = currentArchitecture;
if (!(pluginLibArchitectures & containerArchitectures & currentArchitecture)) {
// Prefererence in order: x86_64, i386, PPC. The only particularly important thing
// about this order is that we'll prefer 64-bit architectures first.
if (base::PROCESS_ARCH_X86_64 & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_X86_64;
}
else if (base::PROCESS_ARCH_I386 & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_I386;
}
else if (base::PROCESS_ARCH_PPC & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_PPC;
}
else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_ARM;
}
else {
return false;
}
}
vector<string> args;
args.push_back(MungePluginDsoPath(mPluginFilePath));
return SyncLaunch(args, timeoutMs);
return SyncLaunch(args, timeoutMs, selectedArchitecture);
}
void

Просмотреть файл

@ -57,6 +57,29 @@ struct kinfo_proc;
namespace base {
// These can be used in a 32-bit bitmask.
enum ProcessArchitecture {
PROCESS_ARCH_I386 = 0x1,
PROCESS_ARCH_X86_64 = 0x2,
PROCESS_ARCH_PPC = 0x4,
PROCESS_ARCH_ARM = 0x8
};
static ProcessArchitecture GetCurrentProcessArchitecture()
{
base::ProcessArchitecture currentArchitecture;
#if defined(ARCH_CPU_X86)
currentArchitecture = base::PROCESS_ARCH_I386;
#elif defined(ARCH_CPU_X86_64)
currentArchitecture = base::PROCESS_ARCH_X86_64;
#elif defined(ARCH_CPU_PPC)
currentArchitecture = base::PROCESS_ARCH_PPC;
#elif defined(ARCH_CPU_ARMEL)
currentArchitecture = base::PROCESS_ARCH_ARM;
#endif
return currentArchitecture;
}
// A minimalistic but hopefully cross-platform set of exit codes.
// Do not change the enumeration values or you will break third-party
// installers.
@ -140,7 +163,8 @@ typedef std::map<std::string, std::string> environment_map;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle);
bool wait, ProcessHandle* process_handle,
ProcessArchitecture arch=GetCurrentProcessArchitecture());
#endif
// Executes the application specified by cl. This function delegates to one

Просмотреть файл

@ -46,7 +46,8 @@ bool LaunchApp(const std::vector<std::string>& argv,
#if defined(CHROMIUM_MOZILLA_BUILD)
const environment_map& env_vars_to_set,
#endif
bool wait, ProcessHandle* process_handle) {
bool wait, ProcessHandle* process_handle,
ProcessArchitecture arch) {
pid_t pid = fork();
if (pid < 0)
return false;

Просмотреть файл

@ -22,6 +22,14 @@
namespace base {
void FreeEnvVarsArray(char* array[], int length)
{
for (int i = 0; i < length; i++) {
free(array[i]);
}
delete[] array;
}
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
@ -32,7 +40,8 @@ bool LaunchApp(const std::vector<std::string>& argv,
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle) {
bool wait, ProcessHandle* process_handle,
ProcessArchitecture arch) {
bool retval = true;
char* argv_copy[argv.size() + 1];
@ -75,10 +84,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
FreeEnvVarsArray(vars, varsLen);
return false;
}
@ -97,30 +103,59 @@ bool LaunchApp(const std::vector<std::string>& argv,
} else {
if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
posix_spawn_file_actions_destroy(&file_actions);
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
FreeEnvVarsArray(vars, varsLen);
return false;
}
}
}
// Set up the CPU preference array.
cpu_type_t cpu_types[1];
switch (arch) {
case PROCESS_ARCH_I386:
cpu_types[0] = CPU_TYPE_X86;
break;
case PROCESS_ARCH_X86_64:
cpu_types[0] = CPU_TYPE_X86_64;
break;
case PROCESS_ARCH_PPC:
cpu_types[0] = CPU_TYPE_POWERPC;
default:
cpu_types[0] = CPU_TYPE_ANY;
break;
}
// Initialize spawn attributes.
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0) {
FreeEnvVarsArray(vars, varsLen);
return false;
}
// Set spawn attributes.
size_t attr_count = 1;
size_t attr_ocount = 0;
if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 ||
attr_ocount != attr_count) {
FreeEnvVarsArray(vars, varsLen);
posix_spawnattr_destroy(&spawnattr);
return false;
}
int pid = 0;
int spawn_succeeded = (posix_spawnp(&pid,
argv_copy[0],
&file_actions,
NULL,
&spawnattr,
argv_copy,
vars) == 0);
for(int j = 0; j < varsLen; j++) {
free(vars[j]);
}
delete[] vars;
FreeEnvVarsArray(vars, varsLen);
posix_spawn_file_actions_destroy(&file_actions);
posix_spawnattr_destroy(&spawnattr);
bool process_handle_valid = pid > 0;
if (!spawn_succeeded || !process_handle_valid) {
retval = false;

Просмотреть файл

@ -46,6 +46,7 @@
#ifdef XP_MACOSX
#include "chrome/common/mach_ipc_mac.h"
#include "base/rand_util.h"
#include "nsILocalFileMac.h"
#endif
#include "prprf.h"
@ -119,6 +120,122 @@ GeckoChildProcessHost::~GeckoChildProcessHost()
#endif
}
void GetPathToBinary(FilePath& exePath)
{
#if defined(OS_WIN)
exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
exePath = exePath.DirName();
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
#elif defined(OS_POSIX)
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
nsCOMPtr<nsIFile> greDir;
nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
if (NS_SUCCEEDED(rv)) {
nsCString path;
greDir->GetNativePath(path);
exePath = FilePath(path.get());
}
else {
exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
exePath = exePath.DirName();
}
#ifdef OS_MACOSX
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
#endif
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
#endif
}
#ifdef XP_MACOSX
class AutoCFTypeObject {
public:
AutoCFTypeObject(CFTypeRef object)
{
mObject = object;
}
~AutoCFTypeObject()
{
::CFRelease(mObject);
}
private:
CFTypeRef mObject;
};
#endif
nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32 *result)
{
*result = 0;
#ifdef XP_MACOSX
CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8*)path,
strlen(path),
false);
if (!url) {
return NS_ERROR_FAILURE;
}
AutoCFTypeObject autoPluginContainerURL(url);
CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url);
if (!pluginContainerArchs) {
return NS_ERROR_FAILURE;
}
AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs);
CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs);
for (CFIndex i = 0; i < pluginArchCount; i++) {
CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i));
int currentArchInt = 0;
if (!::CFNumberGetValue(currentArch, kCFNumberIntType, &currentArchInt)) {
continue;
}
switch (currentArchInt) {
case kCFBundleExecutableArchitectureI386:
*result |= base::PROCESS_ARCH_I386;
break;
case kCFBundleExecutableArchitectureX86_64:
*result |= base::PROCESS_ARCH_X86_64;
break;
case kCFBundleExecutableArchitecturePPC:
*result |= base::PROCESS_ARCH_PPC;
break;
default:
break;
}
}
return (*result ? NS_OK : NS_ERROR_FAILURE);
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
uint32 GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
{
#ifdef XP_MACOSX
if (type == GeckoProcessType_Plugin) {
// Cache this, it shouldn't ever change.
static uint32 pluginContainerArchs = 0;
if (pluginContainerArchs == 0) {
FilePath exePath;
GetPathToBinary(exePath);
nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
if (NS_FAILED(rv) || pluginContainerArchs == 0) {
pluginContainerArchs = base::GetCurrentProcessArchitecture();
}
}
return pluginContainerArchs;
}
#endif
return base::GetCurrentProcessArchitecture();
}
#ifdef XP_WIN
void GeckoChildProcessHost::InitWindowsGroupID()
{
@ -143,7 +260,7 @@ void GeckoChildProcessHost::InitWindowsGroupID()
#endif
bool
GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs)
GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
{
#ifdef XP_WIN
InitWindowsGroupID();
@ -157,7 +274,7 @@ GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTime
ioLoop->PostTask(FROM_HERE,
NewRunnableMethod(this,
&GeckoChildProcessHost::PerformAsyncLaunch,
aExtraOpts));
aExtraOpts, arch));
// NB: this uses a different mechanism than the chromium parent
// class.
MonitorAutoEnter mon(mMonitor);
@ -194,7 +311,7 @@ GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
ioLoop->PostTask(FROM_HERE,
NewRunnableMethod(this,
&GeckoChildProcessHost::PerformAsyncLaunch,
aExtraOpts));
aExtraOpts, base::GetCurrentProcessArchitecture()));
// This may look like the sync launch wait, but we only delay as
// long as it takes to create the channel.
@ -217,7 +334,7 @@ GeckoChildProcessHost::InitializeChannel()
}
bool
GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
{
// FIXME/cjones: make this work from non-IO threads, too
@ -246,18 +363,14 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
// and passing wstrings from one config to the other is unsafe. So
// we split the logic here.
FilePath exePath;
#if defined(OS_LINUX) || defined(OS_MACOSX)
base::environment_map newEnvVars;
#endif
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
nsCOMPtr<nsIFile> greDir;
nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
if (NS_SUCCEEDED(rv)) {
nsCString path;
greDir->GetNativePath(path);
exePath = FilePath(path.get());
#ifdef OS_LINUX
#ifdef ANDROID
path += "/lib";
@ -267,18 +380,10 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
#endif
}
else {
exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
exePath = exePath.DirName();
}
#ifdef OS_MACOSX
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
#endif
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
FilePath exePath;
GetPathToBinary(exePath);
#ifdef ANDROID
// The java wrapper unpacks this for us but can't make it executable
@ -350,7 +455,7 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
#if defined(OS_LINUX) || defined(OS_MACOSX)
newEnvVars,
#endif
false, &process);
false, &process, arch);
#ifdef XP_MACOSX
// Wait for the child process to send us its 'task_t' data.
@ -394,11 +499,8 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
//--------------------------------------------------
#elif defined(OS_WIN)
FilePath exePath =
FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
exePath = exePath.DirName();
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
FilePath exePath;
GetPathToBinary(exePath);
CommandLine cmdLine(exePath.ToWStringHack());
cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());

Просмотреть файл

@ -64,9 +64,16 @@ public:
~GeckoChildProcessHost();
bool SyncLaunch(std::vector<std::string> aExtraOpts=std::vector<std::string>(), int32 timeoutMs=0);
static nsresult GetArchitecturesForBinary(const char *path, uint32 *result);
static uint32 GetSupportedArchitecturesForProcessType(GeckoProcessType type);
bool SyncLaunch(std::vector<std::string> aExtraOpts=std::vector<std::string>(),
int32 timeoutMs=0,
base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
bool AsyncLaunch(std::vector<std::string> aExtraOpts=std::vector<std::string>());
bool PerformAsyncLaunch(std::vector<std::string> aExtraOpts=std::vector<std::string>());
bool PerformAsyncLaunch(std::vector<std::string> aExtraOpts=std::vector<std::string>(),
base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
virtual void OnChannelConnected(int32 peer_pid);
virtual void OnMessageReceived(const IPC::Message& aMsg);

Просмотреть файл

@ -45,6 +45,11 @@
by Patrick C. Beard.
*/
#ifdef MOZ_IPC
#include "GeckoChildProcessHost.h"
#include "base/process_util.h"
#endif
#include "prlink.h"
#include "prnetdb.h"
#include "nsXPCOM.h"
@ -103,13 +108,13 @@ static nsresult toCFURLRef(nsIFile* file, CFURLRef& outURL)
PRBool nsPluginsDir::IsPluginFile(nsIFile* file)
{
nsCString temp;
file->GetNativeLeafName(temp);
nsCString fileName;
file->GetNativeLeafName(fileName);
/*
* Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
* 10.5.3 (see bug 436575).
*/
if (!strcmp(temp.get(), "VerifiedDownloadPlugin.plugin")) {
if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) {
NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
return PR_FALSE;
}
@ -125,7 +130,27 @@ PRBool nsPluginsDir::IsPluginFile(nsIFile* file)
UInt32 packageType, packageCreator;
::CFBundleGetPackageInfo(pluginBundle, &packageType, &packageCreator);
if (packageType == 'BRPL' || packageType == 'IEPL' || packageType == 'NSPL') {
#ifdef MOZ_IPC
// Get path to plugin as a C string.
char executablePath[PATH_MAX];
executablePath[0] = '\0';
if (!::CFURLGetFileSystemRepresentation(pluginURL, true, (UInt8*)&executablePath, PATH_MAX)) {
executablePath[0] = '\0';
}
uint32 pluginLibArchitectures;
nsresult rv = mozilla::ipc::GeckoChildProcessHost::GetArchitecturesForBinary(executablePath, &pluginLibArchitectures);
if (NS_FAILED(rv)) {
return PR_FALSE;
}
uint32 containerArchitectures = mozilla::ipc::GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
// Consider the plugin architecture valid if there is any overlap in the masks.
isPluginFile = !!(containerArchitectures & pluginLibArchitectures);
#else
isPluginFile = !!::CFBundlePreflightExecutable(pluginBundle, NULL);
#endif
}
::CFRelease(pluginBundle);
}