mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Flatten and simplify Servo's preferences code. In addition, have both preferences and options passed in as arguments to `Servo::new()` and make sure not to use the globally set preferences in `servoshell` (as much as possible now). Instead of a complex procedural macro to generate preferences, just expose a very simple derive macro that adds string based getters and setters. - All command-line parsing is moved to servoshell. - There is no longer the concept of a missing preference. - Preferences no longer have to be part of the resources bundle because they now have reasonable default values. - servoshell specific preferences are no longer part of the preferences exposed by the Servo API. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
286 lines
8.9 KiB
Rust
286 lines
8.9 KiB
Rust
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
#[cfg(not(windows))]
|
|
use std::env;
|
|
use std::ffi::OsStr;
|
|
use std::process;
|
|
|
|
#[cfg(any(
|
|
target_os = "macos",
|
|
all(
|
|
not(target_os = "windows"),
|
|
not(target_os = "ios"),
|
|
not(target_os = "android"),
|
|
not(target_env = "ohos"),
|
|
not(target_arch = "arm"),
|
|
not(target_arch = "aarch64")
|
|
)
|
|
))]
|
|
use gaol::profile::{Operation, PathPattern, Profile};
|
|
use ipc_channel::Error;
|
|
use serde::{Deserialize, Serialize};
|
|
use servo_config::opts::Opts;
|
|
use servo_config::prefs::Preferences;
|
|
|
|
use crate::pipeline::UnprivilegedPipelineContent;
|
|
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum UnprivilegedContent {
|
|
Pipeline(UnprivilegedPipelineContent),
|
|
ServiceWorker(ServiceWorkerUnprivilegedContent),
|
|
}
|
|
|
|
impl UnprivilegedContent {
|
|
pub fn opts(&self) -> Opts {
|
|
match self {
|
|
UnprivilegedContent::Pipeline(content) => content.opts(),
|
|
UnprivilegedContent::ServiceWorker(content) => content.opts(),
|
|
}
|
|
}
|
|
|
|
pub fn prefs(&self) -> &Preferences {
|
|
match self {
|
|
UnprivilegedContent::Pipeline(content) => content.prefs(),
|
|
UnprivilegedContent::ServiceWorker(content) => content.prefs(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Our content process sandbox profile on Mac. As restrictive as possible.
|
|
#[cfg(target_os = "macos")]
|
|
pub fn content_process_sandbox_profile() -> Profile {
|
|
use std::path::PathBuf;
|
|
|
|
use embedder_traits::resources;
|
|
use gaol::platform;
|
|
|
|
let mut operations = vec![
|
|
Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/dev/urandom"))),
|
|
Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/Library/Fonts"))),
|
|
Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/System/Library/Fonts"))),
|
|
Operation::FileReadAll(PathPattern::Subpath(PathBuf::from(
|
|
"/System/Library/Frameworks/ApplicationServices.framework",
|
|
))),
|
|
Operation::FileReadAll(PathPattern::Subpath(PathBuf::from(
|
|
"/System/Library/Frameworks/CoreGraphics.framework",
|
|
))),
|
|
Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/"))),
|
|
Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/Library"))),
|
|
Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/System"))),
|
|
Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/etc"))),
|
|
Operation::SystemInfoRead,
|
|
Operation::PlatformSpecific(platform::macos::Operation::MachLookup(
|
|
b"com.apple.FontServer".to_vec(),
|
|
)),
|
|
];
|
|
|
|
operations.extend(
|
|
resources::sandbox_access_files()
|
|
.into_iter()
|
|
.map(|p| Operation::FileReadAll(PathPattern::Literal(p))),
|
|
);
|
|
operations.extend(
|
|
resources::sandbox_access_files_dirs()
|
|
.into_iter()
|
|
.map(|p| Operation::FileReadAll(PathPattern::Subpath(p))),
|
|
);
|
|
|
|
Profile::new(operations).expect("Failed to create sandbox profile!")
|
|
}
|
|
|
|
/// Our content process sandbox profile on Linux. As restrictive as possible.
|
|
#[cfg(all(
|
|
not(target_os = "macos"),
|
|
not(target_os = "windows"),
|
|
not(target_os = "ios"),
|
|
not(target_os = "android"),
|
|
not(target_env = "ohos"),
|
|
not(target_arch = "arm"),
|
|
not(target_arch = "aarch64")
|
|
))]
|
|
pub fn content_process_sandbox_profile() -> Profile {
|
|
use std::path::PathBuf;
|
|
|
|
use embedder_traits::resources;
|
|
|
|
let mut operations = vec![Operation::FileReadAll(PathPattern::Literal(PathBuf::from(
|
|
"/dev/urandom",
|
|
)))];
|
|
|
|
operations.extend(
|
|
resources::sandbox_access_files()
|
|
.into_iter()
|
|
.map(|p| Operation::FileReadAll(PathPattern::Literal(p))),
|
|
);
|
|
operations.extend(
|
|
resources::sandbox_access_files_dirs()
|
|
.into_iter()
|
|
.map(|p| Operation::FileReadAll(PathPattern::Subpath(p))),
|
|
);
|
|
|
|
Profile::new(operations).expect("Failed to create sandbox profile!")
|
|
}
|
|
|
|
#[cfg(any(
|
|
target_os = "windows",
|
|
target_os = "ios",
|
|
target_os = "android",
|
|
target_env = "ohos",
|
|
target_arch = "arm",
|
|
|
|
// exclude apple arm devices
|
|
all(target_arch = "aarch64", not(target_os = "macos"))
|
|
))]
|
|
pub fn content_process_sandbox_profile() {
|
|
log::error!("Sandboxed multiprocess is not supported on this platform.");
|
|
process::exit(1);
|
|
}
|
|
|
|
#[cfg(any(
|
|
target_os = "android",
|
|
target_env = "ohos",
|
|
target_arch = "arm",
|
|
all(target_arch = "aarch64", not(target_os = "windows"))
|
|
))]
|
|
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
|
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
|
// Note that this function can panic, due to process creation,
|
|
// avoiding this panic would require a mechanism for dealing
|
|
// with low-resource scenarios.
|
|
let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
|
|
.expect("Failed to create IPC one-shot server.");
|
|
|
|
let path_to_self = env::current_exe().expect("Failed to get current executor.");
|
|
let mut child_process = process::Command::new(path_to_self);
|
|
setup_common(&mut child_process, token);
|
|
|
|
#[allow(clippy::zombie_processes)]
|
|
let _ = child_process
|
|
.spawn()
|
|
.expect("Failed to start unsandboxed child process!");
|
|
|
|
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
|
sender.send(content)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(all(
|
|
not(target_os = "windows"),
|
|
not(target_os = "ios"),
|
|
not(target_os = "android"),
|
|
not(target_env = "ohos"),
|
|
not(target_arch = "arm"),
|
|
not(target_arch = "aarch64")
|
|
))]
|
|
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
|
use gaol::sandbox::{self, Sandbox, SandboxMethods};
|
|
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
|
|
|
// TODO: Move this impl out of the function. It is only currently here to avoid
|
|
// duplicating the feature flagging.
|
|
#[allow(non_local_definitions)]
|
|
impl CommandMethods for gaol::sandbox::Command {
|
|
fn arg<T>(&mut self, arg: T)
|
|
where
|
|
T: AsRef<OsStr>,
|
|
{
|
|
self.arg(arg);
|
|
}
|
|
|
|
fn env<T, U>(&mut self, key: T, val: U)
|
|
where
|
|
T: AsRef<OsStr>,
|
|
U: AsRef<OsStr>,
|
|
{
|
|
self.env(key, val);
|
|
}
|
|
}
|
|
|
|
// Note that this function can panic, due to process creation,
|
|
// avoiding this panic would require a mechanism for dealing
|
|
// with low-resource scenarios.
|
|
let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
|
|
.expect("Failed to create IPC one-shot server.");
|
|
|
|
// If there is a sandbox, use the `gaol` API to create the child process.
|
|
if content.opts().sandbox {
|
|
let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
|
|
setup_common(&mut command, token);
|
|
|
|
let profile = content_process_sandbox_profile();
|
|
let _ = Sandbox::new(profile)
|
|
.start(&mut command)
|
|
.expect("Failed to start sandboxed child process!");
|
|
} else {
|
|
let path_to_self = env::current_exe().expect("Failed to get current executor.");
|
|
let mut child_process = process::Command::new(path_to_self);
|
|
setup_common(&mut child_process, token);
|
|
|
|
#[allow(clippy::zombie_processes)]
|
|
let _ = child_process
|
|
.spawn()
|
|
.expect("Failed to start unsandboxed child process!");
|
|
}
|
|
|
|
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
|
sender.send(content)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "ios"))]
|
|
pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<(), Error> {
|
|
log::error!("Multiprocess is not supported on Windows or iOS.");
|
|
process::exit(1);
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
fn setup_common<C: CommandMethods>(command: &mut C, token: String) {
|
|
C::arg(command, "--content-process");
|
|
C::arg(command, token);
|
|
|
|
if let Ok(value) = env::var("RUST_BACKTRACE") {
|
|
C::env(command, "RUST_BACKTRACE", value);
|
|
}
|
|
|
|
if let Ok(value) = env::var("RUST_LOG") {
|
|
C::env(command, "RUST_LOG", value);
|
|
}
|
|
}
|
|
|
|
/// A trait to unify commands launched as multiprocess with or without a sandbox.
|
|
#[allow(dead_code)]
|
|
trait CommandMethods {
|
|
/// A command line argument.
|
|
fn arg<T>(&mut self, arg: T)
|
|
where
|
|
T: AsRef<OsStr>;
|
|
|
|
/// An environment variable.
|
|
fn env<T, U>(&mut self, key: T, val: U)
|
|
where
|
|
T: AsRef<OsStr>,
|
|
U: AsRef<OsStr>;
|
|
}
|
|
|
|
impl CommandMethods for process::Command {
|
|
fn arg<T>(&mut self, arg: T)
|
|
where
|
|
T: AsRef<OsStr>,
|
|
{
|
|
self.arg(arg);
|
|
}
|
|
|
|
fn env<T, U>(&mut self, key: T, val: U)
|
|
where
|
|
T: AsRef<OsStr>,
|
|
U: AsRef<OsStr>,
|
|
{
|
|
self.env(key, val);
|
|
}
|
|
}
|