Files
OBS-WebKitGTK/tests/shared-profile-test.c
2026-07-20 10:23:57 +05:00

204 lines
6.5 KiB
C

#include "protocol.h"
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <spawn.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
struct view {
pid_t pid;
int control;
int frames;
int shared_fd;
struct owb_shared_frames *shared;
size_t shared_size;
};
static bool write_all(int fd, const void *data, size_t size)
{
const uint8_t *cursor = data;
while (size) {
ssize_t count = write(fd, cursor, size);
if (count < 0 && errno == EINTR)
continue;
if (count <= 0)
return false;
cursor += (size_t)count;
size -= (size_t)count;
}
return true;
}
static void serve_pages(int server)
{
static const char page[] =
"<!doctype html><style>html,body{margin:0;width:100%;height:100%;background:rgb(2,3,5)}</style>"
"<script>const p=new URL(location.href),t=p.searchParams.get('token'),"
"k='owb-shared-profile-test',b=new BroadcastChannel(k);"
"if(p.pathname==='/writer'){setInterval(()=>{localStorage.setItem(k,t);b.postMessage(t)},100)}"
"else{let heard=false;b.onmessage=e=>{if(e.data===t)heard=true};"
"setInterval(()=>{if(heard&&localStorage.getItem(k)===t)"
"document.body.style.background='rgb(17,231,43)'},50)}</script>";
char response[2048];
int length = snprintf(response, sizeof(response),
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-store\r\n"
"Connection: close\r\nContent-Length: %zu\r\n\r\n%s",
strlen(page), page);
for (;;) {
int client = accept(server, NULL, NULL);
if (client < 0 && errno == EINTR)
continue;
if (client < 0)
_exit(0);
char request[1024];
ssize_t ignored = read(client, request, sizeof(request));
(void)ignored;
write_all(client, response, (size_t)length);
close(client);
}
}
static bool start_view(struct view *view, const char *renderer, const char *url)
{
memset(view, 0, sizeof(*view));
view->pid = 0;
view->control = view->frames = view->shared_fd = -1;
int control[2] = {-1, -1};
int frames[2] = {-1, -1};
if (socketpair(AF_UNIX, SOCK_STREAM, 0, control) != 0 || pipe(frames) != 0)
return false;
const uint32_t width = 64, height = 64;
view->shared_size = owb_shared_size(width, height);
view->shared_fd = memfd_create("shared-profile-test", MFD_CLOEXEC);
if (view->shared_fd < 0 || ftruncate(view->shared_fd, (off_t)view->shared_size) != 0)
return false;
view->shared = mmap(NULL, view->shared_size, PROT_READ | PROT_WRITE, MAP_SHARED,
view->shared_fd, 0);
if (view->shared == MAP_FAILED)
return false;
memset(view->shared, 0, sizeof(*view->shared));
view->shared->magic = OWB_SHARED_MAGIC;
view->shared->version = OWB_PROTOCOL_VERSION;
view->shared->width = width;
view->shared->height = height;
view->shared->stride = width * 4;
view->shared->slot_count = OWB_SHARED_FRAME_SLOTS;
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);
posix_spawn_file_actions_adddup2(&actions, control[1], STDIN_FILENO);
posix_spawn_file_actions_adddup2(&actions, frames[1], STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&actions, view->shared_fd, OWB_SHARED_FRAME_FD);
char *arguments[] = {(char *)renderer, NULL};
int error = posix_spawn(&view->pid, renderer, &actions, NULL, arguments, environ);
posix_spawn_file_actions_destroy(&actions);
close(control[1]);
close(frames[1]);
if (error)
return false;
view->control = control[0];
view->frames = frames[0];
struct owb_config_header config = {
.magic = OWB_CONFIG_MAGIC, .version = OWB_PROTOCOL_VERSION,
.width = width, .height = height, .fps = 20, .transparent = 0,
.zoom_percent = 100,
.hardware_acceleration = 1, .url_length = (uint32_t)strlen(url), .css_length = 0,
};
return write_all(view->control, &config, sizeof(config)) &&
write_all(view->control, url, strlen(url));
}
static int stop_view(struct view *view)
{
int status = 0;
if (view->control >= 0) {
shutdown(view->control, SHUT_RDWR);
close(view->control);
}
if (view->frames >= 0)
close(view->frames);
if (view->shared && view->shared != MAP_FAILED)
munmap(view->shared, view->shared_size);
if (view->shared_fd >= 0)
close(view->shared_fd);
if (view->pid > 0) {
kill(view->pid, SIGTERM);
while (waitpid(view->pid, &status, 0) < 0 && errno == EINTR) {
}
}
return status;
}
int main(int argc, char **argv)
{
if (argc != 2)
return 2;
int server = socket(AF_INET, SOCK_STREAM, 0);
int enabled = 1;
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled));
struct sockaddr_in address = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
if (server < 0 || bind(server, (struct sockaddr *)&address, sizeof(address)) != 0 ||
listen(server, 16) != 0)
return 77;
socklen_t address_size = sizeof(address);
if (getsockname(server, (struct sockaddr *)&address, &address_size) != 0)
return 2;
pid_t http_server = fork();
if (http_server == 0)
serve_pages(server);
close(server);
char writer_url[256], reader_url[256];
unsigned port = ntohs(address.sin_port);
snprintf(writer_url, sizeof(writer_url), "http://127.0.0.1:%u/writer?token=%ld", port,
(long)getpid());
snprintf(reader_url, sizeof(reader_url), "http://127.0.0.1:%u/reader?token=%ld", port,
(long)getpid());
struct view writer, reader;
bool started_writer = start_view(&writer, argv[1], writer_url);
bool started_reader = started_writer && start_view(&reader, argv[1], reader_url);
bool shared = false;
for (unsigned attempt = 0; started_reader && attempt < 200; attempt++) {
struct pollfd item = {.fd = reader.frames, .events = POLLIN};
if (poll(&item, 1, 100) <= 0)
continue;
uint8_t slot;
if (read(reader.frames, &slot, sizeof(slot)) != 1 || slot >= OWB_SHARED_FRAME_SLOTS)
continue;
uint8_t *frame = owb_shared_slot(reader.shared, slot);
size_t center = ((size_t)32 * 64 + 32) * 4;
uint8_t *bgra = frame + center;
if (bgra[0] < 55 && bgra[1] > 210 && bgra[2] < 35 && bgra[3] == 255) {
shared = true;
break;
}
}
int reader_status = started_reader ? stop_view(&reader) : 0;
int writer_status = started_writer ? stop_view(&writer) : 0;
kill(http_server, SIGTERM);
waitpid(http_server, NULL, 0);
if (!started_writer || !started_reader)
return 2;
if ((WIFEXITED(reader_status) && WEXITSTATUS(reader_status) == 5) ||
(WIFEXITED(writer_status) && WEXITSTATUS(writer_status) == 5))
return 77;
if (!shared) {
fprintf(stderr, "two renderer views did not share localStorage and BroadcastChannel\n");
return 1;
}
return 0;
}