Added Wayland proxy cache (mzbz#1743144)

This commit is contained in:
Martin Stransky 2023-12-19 09:56:23 +01:00
parent 722aeaae74
commit d14fa011f0
4 changed files with 979 additions and 1 deletions

834
D196554.diff Normal file
View File

@ -0,0 +1,834 @@
diff --git a/third_party/wayland-proxy/moz.build b/third_party/wayland-proxy/moz.build
new file mode 100644
--- /dev/null
+++ b/third_party/wayland-proxy/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Widget: Gtk")
+
+SOURCES += [
+ "wayland-proxy.cpp",
+]
+EXPORTS += [
+ "wayland-proxy.h",
+]
+FINAL_LIBRARY = "xul"
diff --git a/third_party/wayland-proxy/wayland-proxy.h b/third_party/wayland-proxy/wayland-proxy.h
new file mode 100644
--- /dev/null
+++ b/third_party/wayland-proxy/wayland-proxy.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+#ifndef _wayland_proxy_h_
+#define _wayland_proxy_h_
+
+#include <poll.h>
+#include <vector>
+#include <fcntl.h>
+#include <atomic>
+
+typedef unsigned char byte;
+
+class ProxiedConnection;
+
+class WaylandProxy {
+ public:
+
+ static std::unique_ptr<WaylandProxy> Create();
+
+ bool RunChildApplication(char* argv[]);
+ bool RunThread();
+
+ void SetWaylandDisplay();
+ void SetVerbose(bool aVerbose);
+
+ ~WaylandProxy();
+
+ private:
+ bool Init();
+ void Run();
+
+ void SetWaylandProxyDisplay();
+ static void* RunProxyThread(WaylandProxy* aProxy);
+
+ bool SetupWaylandDisplays();
+ bool StartProxyServer();
+ bool IsChildAppTerminated();
+
+ bool PollConnections();
+ bool ProcessConnections();
+
+ private:
+ // List of all Compositor <-> Application connections
+ std::vector<std::unique_ptr<ProxiedConnection>> mConnections;
+ int mProxyServerSocket = -1;
+ pid_t mApplicationPID = 0;
+ std::atomic<bool> mThreadRunning = false;
+ pthread_t mThread;
+};
+
+#endif // _wayland_proxy_h_
diff --git a/third_party/wayland-proxy/wayland-proxy.cpp b/third_party/wayland-proxy/wayland-proxy.cpp
new file mode 100644
--- /dev/null
+++ b/third_party/wayland-proxy/wayland-proxy.cpp
@@ -0,0 +1,731 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+// This code is based on Rust implementation at
+// https://github.com/the8472/weyland-p5000
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <spawn.h>
+#include <poll.h>
+#include <vector>
+#include <cerrno>
+#include <fcntl.h>
+#include <unistd.h>
+#include <memory>
+#include <cassert>
+#include <pthread.h>
+#include <sched.h>
+
+#include "wayland-proxy.h"
+
+// The maximum number of fds libwayland can recvmsg at once
+#define MAX_LIBWAY_FDS 28
+#define MAX_DATA_SIZE 4096
+#define POLL_TIMEOUT 30000
+
+// sockaddr_un has hardcoded max len of sun_path
+#define MAX_WAYLAND_DISPLAY_NAME_LEN 108
+
+// Name of Wayland display provided by compositor
+char sWaylandDisplay[MAX_WAYLAND_DISPLAY_NAME_LEN];
+
+// Name of Wayland display provided by us
+char sWaylandProxy[MAX_WAYLAND_DISPLAY_NAME_LEN];
+
+bool sPrintInfo = false;
+
+void Info(const char* aFormat, ...) {
+ if (!sPrintInfo) {
+ return;
+ }
+ va_list args;
+ va_start(args, aFormat);
+ vfprintf(stderr, aFormat, args);
+ va_end(args);
+}
+
+void Warning(const char* aOperation, bool aPrintErrno = true) {
+ fprintf(stderr, "Wayland Proxy warning: %s : %s\n", aOperation,
+ aPrintErrno ? strerror(errno) : "");
+}
+
+void Error(const char* aOperation, bool aPrintErrno = true) {
+ fprintf(stderr, "Wayland Proxy error: %s : %s\n", aOperation,
+ aPrintErrno ? strerror(errno) : "");
+}
+
+void ErrorPlain(const char* aFormat, ...) {
+ va_list args;
+ va_start(args, aFormat);
+ vfprintf(stderr, aFormat, args);
+ va_end(args);
+}
+
+class WaylandMessage {
+ public:
+ bool Write(int aSocket);
+
+ bool Loaded() { return mLoaded && (mFds.size() || mData.size()); }
+ bool Failed() { return mFailed; }
+
+ explicit WaylandMessage(int aSocket) { Read(aSocket); }
+ ~WaylandMessage();
+
+ private:
+ bool Read(int aSocket);
+
+ private:
+ bool mLoaded = false;
+ bool mFailed = false;
+
+ std::vector<int> mFds;
+ std::vector<byte> mData;
+};
+
+class ProxiedConnection {
+ public:
+ bool Init(int aChildSocket);
+
+ struct pollfd* AddToPollFd(struct pollfd* aPfds);
+ struct pollfd* LoadPollFd(struct pollfd* aPfds);
+
+ // Process this connection (send/receive data).
+ // Returns false if connection is broken and should be removed.
+ bool Process();
+
+ ~ProxiedConnection();
+
+ private:
+ // Try to connect to compositor. Returns false in case of fatal error.
+ bool ConnectToCompositor();
+
+ bool TransferOrQueue(
+ int aSourceSocket, int aSourcePollFlags, int aDestSocket,
+ std::vector<std::unique_ptr<WaylandMessage>>* aMessageQueue);
+ bool FlushQueue(int aDestSocket, int aDestPollFlags,
+ std::vector<std::unique_ptr<WaylandMessage>>& aMessageQueue);
+
+ // We don't have connected compositor yet. Try to connect
+ bool mCompositorConnected = false;
+
+ // We're disconnected from app or compositor. We will close this connection.
+ bool mFailed = false;
+
+ int mCompositorSocket = -1;
+ int mCompositorFlags = 0;
+
+ int mApplicationSocket = -1;
+ int mApplicationFlags = 0;
+
+ // Stored proxied data
+ std::vector<std::unique_ptr<WaylandMessage>> mToCompositorQueue;
+ std::vector<std::unique_ptr<WaylandMessage>> mToApplicationQueue;
+};
+
+WaylandMessage::~WaylandMessage() {
+ for (auto const fd : mFds) {
+ close(fd);
+ }
+}
+
+bool WaylandMessage::Read(int aSocket) {
+ // We don't expect WaylandMessage re-read
+ assert(!mLoaded && !mFailed);
+
+ mData.resize(MAX_DATA_SIZE);
+
+ struct msghdr msg = {0};
+ struct iovec iov = {mData.data(), mData.size()};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char cmsgdata[(CMSG_LEN(MAX_LIBWAY_FDS * sizeof(int32_t)))] = {0};
+ msg.msg_control = &cmsgdata;
+ msg.msg_controllen = sizeof(cmsgdata);
+
+ ssize_t ret = recvmsg(aSocket, &msg, MSG_CMSG_CLOEXEC | MSG_DONTWAIT);
+ if (msg.msg_flags & (MSG_CTRUNC | MSG_TRUNC)) {
+ Error("WaylandMessage::Read() data truncated, small buffer?");
+ mFailed = true;
+ return false;
+ }
+
+ if (ret < 1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ // Neither loaded nor failed, we'll try again later
+ Info("WaylandMessage::Write() failed %s\n", strerror(errno));
+ return false;
+ default:
+ Error("WaylandMessage::Write() failed");
+ mFailed = true;
+ return false;
+ }
+ }
+
+ // Set correct data size
+ mData.resize(ret);
+
+ // Read cmsg
+ struct cmsghdr* header = CMSG_FIRSTHDR(&msg);
+ while (header) {
+ struct cmsghdr* next = CMSG_NXTHDR(&msg, header);
+ if (header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS) {
+ header = next;
+ continue;
+ }
+
+ int* data = (int*)CMSG_DATA(header);
+ int filenum = (int)((header->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+ for (int i = 0; i < filenum; i++) {
+#ifdef DEBUG
+ int flags = fcntl(data[i], F_GETFL, 0);
+ if (flags == -1 && errno == EBADF) {
+ Error("WaylandMessage::Read() invalid fd");
+ }
+#endif
+ mFds.push_back(data[i]);
+ }
+ header = next;
+ }
+
+ mLoaded = true;
+ return true;
+}
+
+bool WaylandMessage::Write(int aSocket) {
+ if (!mLoaded || mFailed) {
+ return false;
+ }
+
+ struct msghdr msg = {0};
+ struct iovec iov = {mData.data(), mData.size()};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ int filenum = mFds.size();
+ if (filenum) {
+ if (filenum >= MAX_LIBWAY_FDS) {
+ Error("WaylandMessage::Write() too many files to send\n", false);
+ return false;
+ }
+#ifdef DEBUG
+ for (int i = 0; i < filenum; i++) {
+ int flags = fcntl(mFds[i], F_GETFL, 0);
+ if (flags == -1 && errno == EBADF) {
+ Error("WaylandMessage::Write() invalid fd\n");
+ }
+ }
+#endif
+ union {
+ char buf[CMSG_SPACE(sizeof(int) * MAX_LIBWAY_FDS)];
+ struct cmsghdr align;
+ } cmsgu;
+ memset(cmsgu.buf, 0, sizeof(cmsgu.buf));
+
+ msg.msg_control = cmsgu.buf;
+ msg.msg_controllen = sizeof(cmsgu.buf);
+ msg.msg_controllen = CMSG_SPACE(filenum * sizeof(int));
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(filenum * sizeof(int));
+ memcpy(CMSG_DATA(cmsg), mFds.data(), filenum * sizeof(int));
+ }
+
+ ssize_t ret = sendmsg(aSocket, &msg, MSG_CMSG_CLOEXEC | MSG_DONTWAIT);
+ if (ret < 1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ // Neither loaded nor failed, we'll try again later
+ Info("WaylandMessage::Write() failed %s\n", strerror(errno));
+ return false;
+ default:
+ Warning("WaylandMessage::Write() failed");
+ mFailed = true;
+ return false;
+ }
+ }
+
+ if (ret != (ssize_t)mData.size()) {
+ Info("WaylandMessage::Write() failed to write all data! (%d vs. %d)\n", ret,
+ mData.size());
+ }
+ return true;
+}
+
+ProxiedConnection::~ProxiedConnection() {
+ if (mCompositorSocket != -1) {
+ close(mCompositorSocket);
+ }
+ if (mApplicationSocket != -1) {
+ close(mApplicationSocket);
+ }
+}
+
+bool ProxiedConnection::Init(int aApplicationSocket) {
+ mApplicationSocket = aApplicationSocket;
+ mCompositorSocket =
+ socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (mCompositorSocket == -1) {
+ Error("ConnectToCompositor() socket()");
+ }
+ return mApplicationSocket > 0 && mCompositorSocket > 0;
+}
+
+struct pollfd* ProxiedConnection::AddToPollFd(struct pollfd* aPfds) {
+ // Listen application's requests
+ aPfds->fd = mApplicationSocket;
+ aPfds->events = POLLIN;
+
+ // We're connected and we have data for appplication from compositor.
+ // Add POLLOUT to request write to app socket.
+ if (mCompositorConnected && mToApplicationQueue.size()) {
+ aPfds->events |= POLLOUT;
+ }
+ aPfds++;
+
+ aPfds->fd = mCompositorSocket;
+ // We're waiting for connection or we have data for compositor
+ if (!mCompositorConnected || mToCompositorQueue.size()) {
+ aPfds->events |= POLLOUT;
+ }
+ if (mCompositorConnected) {
+ aPfds->events = POLLIN;
+ }
+ aPfds++;
+
+ return aPfds;
+}
+
+struct pollfd* ProxiedConnection::LoadPollFd(struct pollfd* aPfds) {
+ if (aPfds->fd != mApplicationSocket) {
+ return aPfds;
+ }
+ mApplicationFlags = aPfds->revents;
+ aPfds++;
+ mCompositorFlags = aPfds->revents;
+ aPfds++;
+ return aPfds;
+}
+
+bool ProxiedConnection::ConnectToCompositor() {
+ if (!(mCompositorFlags & POLLOUT)) {
+ // Try again later
+ return true;
+ }
+
+ struct sockaddr_un addr = {};
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, sWaylandDisplay);
+
+ mCompositorConnected =
+ connect(mCompositorSocket, (const struct sockaddr*)&addr,
+ sizeof(struct sockaddr_un)) != -1;
+ if (!mCompositorConnected) {
+ switch (errno) {
+ case EAGAIN:
+ case EALREADY:
+ case ECONNREFUSED:
+ case EINPROGRESS:
+ case EINTR:
+ case EISCONN:
+ case ETIMEDOUT:
+ // We can recover from these errors and try again
+ Warning("ConnectToCompositor() try again");
+ return true;
+ default:
+ Error("ConnectToCompositor() connect()");
+ return false;
+ }
+ }
+ return true;
+}
+
+// Read data from aSourceSocket and try to twite them to aDestSocket.
+// If data write fails, append them to aMessageQueue.
+// Return
+bool ProxiedConnection::TransferOrQueue(
+ int aSourceSocket, int aSourcePollFlags, int aDestSocket,
+ std::vector<std::unique_ptr<WaylandMessage>>* aMessageQueue) {
+ // Don't read if we don't have any data ready
+ if (!(aSourcePollFlags & POLLIN)) {
+ return true;
+ }
+
+ while (1) {
+ int availableData = 0;
+ if (ioctl(aSourceSocket, FIONREAD, &availableData) < 0) {
+ // Broken connection, we're finished here
+ Warning("ProxiedConnection::TransferOrQueue() broken source socket %s\n");
+ return false;
+ }
+ if (availableData == 0) {
+ return true;
+ }
+
+ auto message = std::make_unique<WaylandMessage>(aSourceSocket);
+ if (message->Failed()) {
+ // Failed to read message due to error
+ return false;
+ }
+ if (!message->Loaded()) {
+ // Let's try again
+ return true;
+ }
+ if (!message->Write(aDestSocket)) {
+ if (message->Failed()) {
+ // Failed to write and we can't recover
+ return false;
+ }
+ aMessageQueue->push_back(std::move(message));
+ }
+ }
+}
+
+// Try to flush all data to aMessageQueue.
+bool ProxiedConnection::FlushQueue(
+ int aDestSocket, int aDestPollFlags,
+ std::vector<std::unique_ptr<WaylandMessage>>& aMessageQueue) {
+ // Can't write to destination yet
+ if (!(aDestPollFlags & POLLOUT)) {
+ return true;
+ }
+
+ while (aMessageQueue.size()) {
+ if (!aMessageQueue[0]->Write(aDestSocket)) {
+ return !aMessageQueue[0]->Failed();
+ }
+ aMessageQueue.erase(aMessageQueue.begin());
+ }
+ return true;
+}
+
+bool ProxiedConnection::Process() {
+ if (mFailed) {
+ return false;
+ }
+
+ // Check if appplication is still listening
+ if (mApplicationFlags & (POLLHUP | POLLERR)) {
+ return false;
+ }
+
+ // Check if compositor is still listening
+ if (mCompositorConnected) {
+ if (mCompositorFlags & (POLLHUP | POLLERR)) {
+ return false;
+ }
+ } else {
+ // Try to reconnect to compositor.
+ if (!ConnectToCompositor()) {
+ return false;
+ }
+ // We're not connected yet but ConnectToCompositor() didn't return
+ // fatal error. Try again later.
+ if (!mCompositorConnected) {
+ return true;
+ }
+ }
+
+ mFailed =
+ !TransferOrQueue(mCompositorSocket, mCompositorFlags, mApplicationSocket,
+ &mToApplicationQueue) ||
+ !TransferOrQueue(mApplicationSocket, mApplicationFlags, mCompositorSocket,
+ &mToCompositorQueue) ||
+ !FlushQueue(mCompositorSocket, mCompositorFlags, mToCompositorQueue) ||
+ !FlushQueue(mApplicationSocket, mApplicationFlags, mToApplicationQueue);
+
+ return !mFailed;
+}
+
+bool WaylandProxy::SetupWaylandDisplays() {
+ char* waylandDisplay = getenv("WAYLAND_DISPLAY");
+ if (!waylandDisplay) {
+ Error("Init(), Missing Wayland display, WAYLAND_DISPLAY is empty.", false);
+ return false;
+ }
+
+ char* XDGRuntimeDir = getenv("XDG_RUNTIME_DIR");
+ if (!XDGRuntimeDir) {
+ Error("Init() Missing XDG_RUNTIME_DIR", false);
+ return false;
+ }
+
+ // WAYLAND_DISPLAY can be absolute path
+ if (waylandDisplay[0] == '/') {
+ if (strlen(sWaylandDisplay) >= MAX_WAYLAND_DISPLAY_NAME_LEN) {
+ Error("Init() WAYLAND_DISPLAY is too large.", false);
+ return false;
+ }
+ strcpy(sWaylandDisplay, waylandDisplay);
+ } else {
+ int ret = snprintf(sWaylandDisplay, MAX_WAYLAND_DISPLAY_NAME_LEN, "%s/%s",
+ XDGRuntimeDir, waylandDisplay);
+ if (ret < 0 || ret >= MAX_WAYLAND_DISPLAY_NAME_LEN) {
+ Error("Init() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.", false);
+ return false;
+ }
+ }
+ int ret = snprintf(sWaylandProxy, MAX_WAYLAND_DISPLAY_NAME_LEN,
+ "%s/wayland-proxy-%d", XDGRuntimeDir, getpid());
+ if (ret < 0 || ret >= MAX_WAYLAND_DISPLAY_NAME_LEN) {
+ Error("Init() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.", false);
+ return false;
+ }
+
+ return true;
+}
+
+bool WaylandProxy::StartProxyServer() {
+ mProxyServerSocket =
+ socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (mProxyServerSocket == -1) {
+ Error("StartProxyServer(): failed to create socket");
+ return false;
+ }
+
+ struct sockaddr_un serverName = {0};
+ serverName.sun_family = AF_UNIX;
+ strcpy(serverName.sun_path, sWaylandProxy);
+
+ if (bind(mProxyServerSocket, (struct sockaddr*)&serverName,
+ sizeof(serverName)) == -1) {
+ Error("StartProxyServer(): bind() error");
+ return false;
+ }
+ if (listen(mProxyServerSocket, 128) == -1) {
+ Error("StartProxyServer(): listen() error");
+ return false;
+ }
+
+ return true;
+}
+
+bool WaylandProxy::Init() {
+ if (!SetupWaylandDisplays()) {
+ return false;
+ }
+
+ if (!StartProxyServer()) {
+ return false;
+ }
+ return true;
+}
+
+void WaylandProxy::SetWaylandProxyDisplay() {
+ setenv("WAYLAND_DISPLAY", sWaylandProxy, true);
+}
+
+void WaylandProxy::SetWaylandDisplay() {
+ setenv("WAYLAND_DISPLAY", sWaylandDisplay, true);
+}
+
+bool WaylandProxy::IsChildAppTerminated() {
+ if (!mApplicationPID) {
+ return false;
+ }
+ int status = 0;
+ int ret = waitpid(mApplicationPID, &status, WNOHANG | WUNTRACED | WCONTINUED);
+ if (ret == 0) {
+ return false;
+ }
+ if (ret == mApplicationPID) {
+ // Child application is terminated, so quit too.
+ return true;
+ }
+ bool terminate = (errno == ECHILD);
+ Error("IsChildAppTerminated: waitpid() error");
+ return terminate;
+}
+
+bool WaylandProxy::PollConnections() {
+ int nfds_max = mConnections.size() * 2 + 1;
+
+ struct pollfd pollfds[nfds_max];
+ struct pollfd* addedPollfd = pollfds;
+
+ for (auto const& connection : mConnections) {
+ addedPollfd = connection->AddToPollFd(addedPollfd);
+ }
+
+ // Add extra listening socket
+ addedPollfd->fd = mProxyServerSocket;
+ addedPollfd->events = POLLIN;
+ assert(addedPollfd < pollfds + nfds_max);
+
+ int nfds = (addedPollfd - pollfds) + 1;
+
+ while (1) {
+ int ret = poll(pollfds, nfds, POLL_TIMEOUT);
+ if (ret == 0) {
+ // No change on fds
+ continue;
+ } else if (ret > 0) {
+ // We have FD to read
+ break;
+ } else if (ret == -1) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ if (IsChildAppTerminated()) {
+ return false;
+ }
+ continue;
+ default:
+ Error("Run: poll() error");
+ return false;
+ }
+ }
+ }
+
+ struct pollfd* loadedPollfd = pollfds;
+ for (auto const& connection : mConnections) {
+ loadedPollfd = connection->LoadPollFd(loadedPollfd);
+ }
+
+ assert(loadedPollfd == addedPollfd);
+ assert(loadedPollfd < pollfds + nfds_max);
+
+ // Create a new connection if there's a new client waiting
+ if (loadedPollfd->revents & POLLIN) {
+ Info("WaylandProxy: new child connection\n");
+ int applicationSocket = accept4(loadedPollfd->fd, nullptr, nullptr,
+ SOCK_NONBLOCK | SOCK_CLOEXEC);
+ if (applicationSocket == -1) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ // Try again later
+ break;
+ default:
+ Error("Faild to accept connection from application");
+ return false;
+ }
+ } else {
+ auto connection = std::make_unique<ProxiedConnection>();
+ if (connection->Init(applicationSocket)) {
+ mConnections.push_back(std::move(connection));
+ }
+ }
+ }
+
+ return true;
+}
+
+bool WaylandProxy::ProcessConnections() {
+ std::vector<std::unique_ptr<ProxiedConnection>>::iterator connection;
+ for (connection = mConnections.begin(); connection != mConnections.end();) {
+ if (!(*connection)->Process()) {
+ Info("WaylandProxy: remove connection\n");
+ connection = mConnections.erase(connection);
+ if (!mConnections.size()) {
+ // We removed last connection - quit.
+ Info("WaylandProxy: removed last connection, quit\n");
+ return false;
+ }
+ } else {
+ connection++;
+ }
+ }
+ return true;
+}
+
+void WaylandProxy::Run() {
+ while (!IsChildAppTerminated() && PollConnections() && ProcessConnections())
+ ;
+}
+
+WaylandProxy::~WaylandProxy() {
+ Info("WaylandProxy terminated\n");
+ if (mThreadRunning) {
+ Info("WaylandProxy thread is still running, terminating.\n");
+ pthread_cancel(mThread);
+ pthread_join(mThread, nullptr);
+ }
+ unlink(sWaylandProxy);
+ if (mProxyServerSocket != -1) {
+ close(mProxyServerSocket);
+ }
+}
+
+void* WaylandProxy::RunProxyThread(WaylandProxy* aProxy) {
+#ifdef __linux__
+ pthread_setname_np(pthread_self(), "WaylandProxy");
+#endif
+ aProxy->Run();
+ aProxy->mThreadRunning = false;
+ Info("WaylandProxy thread exited\n");
+ return nullptr;
+}
+
+std::unique_ptr<WaylandProxy> WaylandProxy::Create() {
+ auto proxy = std::make_unique<WaylandProxy>();
+ if (!proxy->Init()) {
+ return nullptr;
+ }
+
+ Info("WaylandProxyCreated, display %s\n", sWaylandProxy);
+ return proxy;
+}
+
+bool WaylandProxy::RunChildApplication(char* argv[]) {
+ if (!argv[0]) {
+ Error("WaylandProxy::RunChildApplication: missing application to run", false);
+ return false;
+ }
+
+ mApplicationPID = fork();
+ if (mApplicationPID == -1) {
+ Error("WaylandProxy::RunChildApplication: fork() error");
+ return false;
+ }
+ if (mApplicationPID == 0) {
+ SetWaylandProxyDisplay();
+ if (execv(argv[0], argv) == -1) {
+ ErrorPlain("WaylandProxy::RunChildApplication: failed to run %s error %s\n", argv[0], strerror(errno));
+ exit(1);
+ }
+ }
+
+ Run();
+ return true;
+}
+
+bool WaylandProxy::RunThread() {
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ return false;
+ }
+
+ sched_param param;
+ if (pthread_attr_getschedparam(&attr, &param) == 0) {
+ param.sched_priority = sched_get_priority_min(SCHED_RR);
+ pthread_attr_setschedparam(&attr, &param);
+ }
+
+ SetWaylandProxyDisplay();
+
+ mThreadRunning = pthread_create(&mThread, nullptr, (void* (*)(void*))RunProxyThread, this) == 0;
+ if (!mThreadRunning) {
+ // If we failed to run proxy thread, set WAYLAND_DISPLAY back.
+ SetWaylandDisplay();
+ }
+
+ pthread_attr_destroy(&attr);
+ return mThreadRunning;
+}
+
+void WaylandProxy::SetVerbose(bool aVerbose) { sPrintInfo = aVerbose; }
diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -23,10 +23,13 @@
DIRS += ["mozgtk"]
if CONFIG["MOZ_WAYLAND"]:
DIRS += ["wayland", "mozwayland"]
+if CONFIG["MOZ_WAYLAND_PROXY"]:
+ DIRS += ["../../third_party/wayland-proxy"]
+
if CONFIG["MOZ_ENABLE_VAAPI"]:
DIRS += ["vaapitest"]
if CONFIG["MOZ_ENABLE_V4L2"]:
DIRS += ["v4l2test"]

109
D196555.diff Normal file
View File

@ -0,0 +1,109 @@
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -343,16 +343,24 @@
# include <gtk/gtk.h>
# ifdef MOZ_WAYLAND
# include <gdk/gdkwayland.h>
# include "mozilla/widget/nsWaylandDisplay.h"
# endif
+# ifdef MOZ_WAYLAND_PROXY
+# include "wayland-proxy.h"
+# endif
# ifdef MOZ_X11
# include <gdk/gdkx.h>
# endif /* MOZ_X11 */
#endif
#include "BinaryPath.h"
+#ifdef MOZ_LOGGING
+# include "mozilla/Logging.h"
+extern mozilla::LazyLogModule gWidgetWaylandLog;
+#endif /* MOZ_LOGGING */
+
#ifdef FUZZING
# include "FuzzerRunner.h"
namespace mozilla {
FuzzerRunner* fuzzerRunner = 0;
@@ -3670,10 +3678,13 @@
class XREMain {
public:
XREMain() = default;
~XREMain() {
+#if defined(MOZ_WAYLAND_PROXY)
+ mWaylandProxy = nullptr;
+#endif
mScopedXPCOM = nullptr;
mAppData = nullptr;
}
int XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig);
@@ -3689,10 +3700,13 @@
nsCOMPtr<nsIFile> mProfLD;
nsCOMPtr<nsIProfileLock> mProfileLock;
#if defined(MOZ_HAS_REMOTE)
RefPtr<nsRemoteService> mRemoteService;
#endif
+#if defined(MOZ_WAYLAND_PROXY)
+ std::unique_ptr<WaylandProxy> mWaylandProxy;
+#endif
UniquePtr<ScopedXPCOMStartup> mScopedXPCOM;
UniquePtr<XREAppData> mAppData;
nsXREDirProvider mDirProvider;
@@ -4709,20 +4723,34 @@
#if defined(MOZ_WIDGET_GTK)
if (!isBackgroundTaskMode && !gfxPlatform::IsHeadless()) {
const char* display_name = nullptr;
bool saveDisplayArg = false;
+ bool waylandEnabled = IsWaylandEnabled();
+# ifdef MOZ_WAYLAND_PROXY
+ auto* proxyEnv = getenv("MOZ_DISABLE_WAYLAND_PROXY");
+ bool disableWaylandProxy = proxyEnv && *proxyEnv;
+ if (!disableWaylandProxy && XRE_IsParentProcess() && waylandEnabled) {
+ mWaylandProxy = WaylandProxy::Create();
+ mWaylandProxy->RunThread();
+# ifdef MOZ_LOGGING
+ if (MOZ_LOG_TEST(gWidgetWaylandLog, mozilla::LogLevel::Debug)) {
+ mWaylandProxy->SetVerbose(true);
+ }
+# endif
+ }
+# endif
+
// display_name is owned by gdk.
display_name = gdk_get_display_arg_name();
// if --display argument is given make sure it's
// also passed to ContentChild::Init() by MOZ_GDK_DISPLAY.
if (display_name) {
SaveWordToEnv("MOZ_GDK_DISPLAY", nsDependentCString(display_name));
saveDisplayArg = true;
}
- bool waylandEnabled = IsWaylandEnabled();
// On Wayland disabled builds read X11 DISPLAY env exclusively
// and don't care about different displays.
if (!waylandEnabled && !display_name) {
display_name = PR_GetEnv("DISPLAY");
if (!display_name) {
@@ -5953,10 +5981,15 @@
// appropriately when necessary.
if (!gfxPlatform::IsHeadless()) {
# ifdef MOZ_WAYLAND
WaylandDisplayRelease();
# endif
+# ifdef MOZ_WAYLAND_PROXY
+ if (mWaylandProxy) {
+ mWaylandProxy = nullptr;
+ }
+# endif
}
#endif
XRE_DeinitCommandLine();

29
D196556.diff Normal file
View File

@ -0,0 +1,29 @@
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -532,10 +532,24 @@
set_config("MOZ_WAYLAND", depends_if(wayland_headers)(lambda _: True))
set_define("MOZ_WAYLAND", depends_if(wayland_headers)(lambda _: True))
+# Wayland proxy is used as load balancer between Firefox and Wayland
+# compositor.
+# ==============================================================
+option("--disable-wayland-proxy", help="Disable Wayland load balancer")
+
+
+@depends("--enable-wayland-proxy", when=toolkit_gtk_wayland)
+def wayland_proxy(value):
+ if value:
+ return True
+
+
+set_config("MOZ_WAYLAND_PROXY", wayland_proxy)
+set_define("MOZ_WAYLAND_PROXY", wayland_proxy)
# Hardware-accelerated video decode with VAAPI and V4L2 on Linux
# ==============================================================
set_config("MOZ_ENABLE_VAAPI", True, when=toolkit_gtk)
set_define("MOZ_ENABLE_VAAPI", True, when=toolkit_gtk)

View File

@ -174,7 +174,7 @@ ExcludeArch: i686
Summary: Mozilla Firefox Web browser
Name: firefox
Version: 121.0
Release: 1%{?pre_tag}%{?dist}
Release: 2%{?pre_tag}%{?dist}
URL: https://www.mozilla.org/firefox/
License: MPLv1.1 or GPLv2+ or LGPLv2+
Source0: https://archive.mozilla.org/pub/firefox/releases/%{version}%{?pre_version}/source/firefox-%{version}%{?pre_version}.source.tar.xz
@ -254,6 +254,9 @@ Patch242: 0026-Add-KDE-integration-to-Firefox.patch
Patch402: mozilla-1196777.patch
Patch407: mozilla-1667096.patch
Patch408: D167159.diff
Patch409: D196554.diff
Patch410: D196555.diff
Patch411: D196556.diff
# PGO/LTO patches
Patch600: pgo.patch
@ -1165,6 +1168,9 @@ fi
#---------------------------------------------------------------------
%changelog
* Tue Dec 19 2023 Martin Stransky <stransky@redhat.com>- 121.0-2
- Added Wayland proxy cache (mzbz#1743144)
* Mon Dec 18 2023 Martin Stransky <stransky@redhat.com>- 121.0-2
- Enable Gnome Shell Search provider for Fedora 40+
- Don't ship firefox-x11 and firefox-wayland on Fedora 40+