blob: 66f61945939dd6d4250ea900dfacc81d0947ac32 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/342213636): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "content/app/ios/appex/child_process_bridge.h"
#include <pthread.h>
#include <xpc/xpc.h>
#include "base/apple/bundle_locations.h"
#include "base/apple/mach_port_rendezvous_ios.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/system/sys_info.h"
#include "content/app/ios/appex/child_process_sandbox.h"
#include "gpu/ipc/common/ios/be_layer_hierarchy_transport.h"
#include "sandbox/policy/switches.h"
class GPUProcessTransport;
// Leaked variables for now.
static size_t g_argc = 0;
static const char** g_argv = nullptr;
static pthread_t g_main_thread;
static id<ChildProcessExtension> g_swift_process;
static xpc_connection_t g_connection;
static std::unique_ptr<GPUProcessTransport> g_gpu_transport;
#define IOS_INIT_EXPORT __attribute__((visibility("default")))
// The embedder must implement this.
extern "C" int ChildProcessMain(int argc, const char** argv);
class GPUProcessTransport : public gpu::BELayerHierarchyTransport {
public:
GPUProcessTransport() { gpu::BELayerHierarchyTransport::SetInstance(this); }
~GPUProcessTransport() override {
gpu::BELayerHierarchyTransport::SetInstance(nullptr);
}
void ForwardBELayerHierarchyToBrowser(
gpu::SurfaceHandle surface_handle,
xpc_object_t ipc_representation) override {
xpc_object_t message = xpc_dictionary_create(nil, nil, 0);
xpc_dictionary_set_string(message, "message", "layerHandle");
xpc_dictionary_set_value(message, "layer", ipc_representation);
xpc_dictionary_set_uint64(message, "handle", surface_handle);
xpc_connection_send_message(g_connection, message);
}
};
extern "C" IOS_INIT_EXPORT void GpuProcessInit() {
g_gpu_transport = std::make_unique<GPUProcessTransport>();
}
extern "C" IOS_INIT_EXPORT void ChildProcessInit(
id<ChildProcessExtension> process) {
// Up two levels: chrome.app/Extensions/chrome_content_process.appex
NSBundle* bundle = [NSBundle bundleWithURL:[[[NSBundle mainBundle].bundleURL
URLByDeletingLastPathComponent]
URLByDeletingLastPathComponent]];
base::apple::SetOverrideFrameworkBundle(bundle);
g_swift_process = process;
}
void* RunMain(void* data) {
ChildProcessMain((int)g_argc, g_argv);
return nullptr;
}
extern "C" IOS_INIT_EXPORT void ChildProcessHandleNewConnection(
xpc_connection_t connection) {
xpc_connection_set_event_handler(connection, ^(xpc_object_t msg) {
xpc_type_t msg_type = xpc_get_type(msg);
CHECK_EQ(msg_type, XPC_TYPE_DICTIONARY);
xpc_object_t args_array = xpc_dictionary_get_array(msg, "args");
g_argc = xpc_array_get_count(args_array);
g_argv = new const char*[g_argc];
for (size_t i = 0; i < g_argc; ++i) {
g_argv[i] = strdup(xpc_array_get_string(args_array, i));
}
// Setup stdout/stderr.
int fd = xpc_dictionary_dup_fd(msg, "stdout");
if (fd != -1) {
dup2(fd, STDOUT_FILENO);
close(fd);
}
fd = xpc_dictionary_dup_fd(msg, "stderr");
if (fd != -1) {
dup2(fd, STDERR_FILENO);
close(fd);
}
// See child_process_launcher_helper_ios.mm for discussion of this
// bookmark data.
size_t tmp_dir_length = 0;
const void* tmp_dir =
xpc_dictionary_get_data(msg, "tmp_dir", &tmp_dir_length);
CHECK(tmp_dir);
NSData* bookmark_temp_dir = [NSData dataWithBytes:tmp_dir
length:tmp_dir_length];
BOOL bookmarkIsStale = NO;
NSError* error = nil;
NSURL* tmp_dir_url =
[NSURL URLByResolvingBookmarkData:bookmark_temp_dir
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:nil
bookmarkDataIsStale:&bookmarkIsStale
error:&error];
CHECK(error == nil) << base::SysNSStringToUTF8(
[error localizedDescription]);
CHECK(tmp_dir_url);
std::string file_path = base::SysNSStringToUTF8(tmp_dir_url.path) + "/";
CHECK_EQ(setenv("TMPDIR", file_path.c_str(), 1), 0);
base::FilePath assigned_path;
CHECK(base::GetTempDir(&assigned_path));
CHECK(assigned_path.value() == file_path);
// The gpu_cache_dir key will only be set for the GPU process extension, but
// this code runs for all extension types.
size_t gpu_cache_bookmark_length = 0;
const void* gpu_cache_bookmark_data = xpc_dictionary_get_data(
msg, "gpu_cache_dir", &gpu_cache_bookmark_length);
if (gpu_cache_bookmark_data) {
CHECK(g_gpu_transport);
// See code in child_process_launcher_helper_ios.mm about this.
const char* browser_container_home =
xpc_dictionary_get_string(msg, "browser_container_home");
CHECK(browser_container_home);
CHECK_EQ(setenv("CFFIXED_USER_HOME", browser_container_home, 1), 0);
// Per Apple, we need to set this for Metal shader cache to work properly.
NSString* gpu_bundle_id = [[NSBundle mainBundle] bundleIdentifier];
CHECK(gpu_bundle_id);
CHECK_EQ(setenv("DIRHELPER_USER_DIR_SUFFIX",
base::SysNSStringToUTF8(gpu_bundle_id).c_str(), 1),
0);
// Open the bookmark for the cache directory which will ensure later
// accesses after the sandbox starts succeed.
NSData* gpu_cache_bookmark =
[NSData dataWithBytes:gpu_cache_bookmark_data
length:gpu_cache_bookmark_length];
NSURL* gpu_cache_url =
[NSURL URLByResolvingBookmarkData:gpu_cache_bookmark
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:nil
bookmarkDataIsStale:&bookmarkIsStale
error:&error];
CHECK(error == nil) << base::SysNSStringToUTF8(
[error localizedDescription]);
CHECK(gpu_cache_url);
}
mach_port_t port = xpc_dictionary_copy_mach_send(msg, "port");
base::apple::ScopedMachSendRight server_port(port);
bool res =
base::MachPortRendezvousClientIOS::Initialize(std::move(server_port));
CHECK(res) << "MachPortRendezvousClient failed";
// TODO(dtapuska): For now we create our own main thread, figure out if we
// can use the ExtensionMain (thread 0) as the main thread but calling
// CFRunLoopRunInMode seems to crash it so we can't enter a nested event
// loop with some objects on the stack.
pthread_create(&g_main_thread, NULL, RunMain, NULL);
});
xpc_connection_activate(connection);
g_connection = connection;
}
namespace content {
void ChildProcessEnterSandbox() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
sandbox::policy::switches::kNoSandbox)) {
return;
}
base::SysInfo::IsLowEndDevice();
// Request the local time before entering the sandbox since that causes a
// crash after the sandbox is entered.
base::Time::Now().LocalMidnight();
[g_swift_process applySandbox];
}
} // namespace content