api: Flatten and simplify Servo preferences (#34966)

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>
This commit is contained in:
Martin Robinson 2025-01-14 14:54:06 +01:00 committed by GitHub
parent c4c85affb5
commit 0e616e0c5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
316 changed files with 2088 additions and 3235 deletions

View file

@ -7,7 +7,6 @@
mod resources;
mod simpleservo;
use std::collections::HashMap;
use std::os::raw::{c_char, c_int, c_void};
use std::sync::Arc;
@ -830,17 +829,6 @@ fn get_options<'local>(
let native_window =
unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) };
// FIXME: enable JIT compilation on 32-bit Android after the startup crash issue (#31134) is fixed.
let prefs = if cfg!(target_pointer_width = "32") {
let mut prefs = HashMap::new();
prefs.insert("js.baseline_interpreter.enabled".to_string(), false.into());
prefs.insert("js.baseline_jit.enabled".to_string(), false.into());
prefs.insert("js.ion.enabled".to_string(), false.into());
Some(prefs)
} else {
None
};
let opts = InitOptions {
args: args.unwrap_or(vec![]),
url,
@ -848,7 +836,6 @@ fn get_options<'local>(
density,
xr_discovery: None,
surfman_integration: simpleservo::SurfmanIntegration::Widget(native_window),
prefs,
};
Ok((opts, log, log_str, gst_debug_str))

View file

@ -16,7 +16,6 @@ impl ResourceReaderInstance {
impl ResourceReaderMethods for ResourceReaderInstance {
fn read(&self, res: Resource) -> Vec<u8> {
Vec::from(match res {
Resource::Preferences => &include_bytes!("../../../../resources/prefs.json")[..],
Resource::HstsPreloadList => {
&include_bytes!("../../../../resources/hsts_preload.json")[..]
},

View file

@ -3,23 +3,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::os::raw::c_void;
use std::rc::Rc;
use getopts::Options;
use servo::base::id::WebViewId;
use servo::compositing::windowing::EmbedderEvent;
use servo::compositing::CompositeTarget;
pub use servo::config::prefs::{add_user_prefs, PrefValue};
use servo::embedder_traits::resources;
/// The EventLoopWaker::wake function will be called from any thread.
/// It will be called to notify embedder that some events are available,
/// and that perform_updates need to be called
pub use servo::embedder_traits::EventLoopWaker;
pub use servo::embedder_traits::{InputMethodType, MediaSessionPlaybackState, PromptResult};
use servo::servo_config::{opts, pref};
use servo::servo_url::ServoUrl;
pub use servo::webrender_api::units::DeviceIntRect;
use servo::webrender_traits::RenderingContext;
@ -31,6 +27,7 @@ use crate::egl::host_trait::HostTrait;
use crate::egl::servo_glue::{
Coordinates, ServoEmbedderCallbacks, ServoGlue, ServoWindowCallbacks,
};
use crate::prefs::{parse_command_line_arguments, ArgumentParsingResult};
thread_local! {
pub static SERVO: RefCell<Option<ServoGlue>> = RefCell::new(None);
@ -44,7 +41,6 @@ pub struct InitOptions {
#[cfg(feature = "webxr")]
pub xr_discovery: Option<webxr::Discovery>,
pub surfman_integration: SurfmanIntegration,
pub prefs: Option<HashMap<String, PrefValue>>,
}
/// Controls how this embedding's rendering will integrate with the embedder.
@ -64,17 +60,24 @@ pub fn init(
crate::init_crypto();
resources::set(Box::new(ResourceReaderInstance::new()));
if let Some(prefs) = init_opts.prefs {
add_user_prefs(prefs);
}
// `parse_command_line_arguments` expects the first argument to be the binary name.
let mut args = mem::replace(&mut init_opts.args, vec![]);
// opts::from_cmdline_args expects the first argument to be the binary name.
args.insert(0, "servo".to_string());
opts::from_cmdline_args(Options::new(), &args);
let (opts, preferences, servoshell_preferences) = match parse_command_line_arguments(args) {
ArgumentParsingResult::ContentProcess(..) => {
unreachable!("Android does not have support for multiprocess yet.")
},
ArgumentParsingResult::ChromeProcess(opts, preferences, servoshell_preferences) => {
(opts, preferences, servoshell_preferences)
},
};
let embedder_url = init_opts.url.as_ref().and_then(|s| ServoUrl::parse(s).ok());
let pref_url = ServoUrl::parse(&pref!(shell.homepage)).ok();
let pref_url = servoshell_preferences
.url
.as_ref()
.and_then(|s| ServoUrl::parse(s).ok());
let blank_url = ServoUrl::parse("about:blank").ok();
let url = embedder_url.or(pref_url).or(blank_url).unwrap();
@ -117,6 +120,8 @@ pub fn init(
));
let servo = Servo::new(
opts,
preferences,
rendering_context.clone(),
embedder_callbacks,
window_callbacks.clone(),
@ -125,7 +130,12 @@ pub fn init(
);
SERVO.with(|s| {
let mut servo_glue = ServoGlue::new(rendering_context, servo, window_callbacks, None);
let mut servo_glue = ServoGlue::new(
rendering_context,
servo,
window_callbacks,
servoshell_preferences,
);
let _ = servo_glue.process_event(EmbedderEvent::NewWebView(url, WebViewId::new()));
*s.borrow_mut() = Some(servo_glue);
});

View file

@ -17,8 +17,6 @@ use servo::embedder_traits::resources;
/// and that perform_updates need to be called
pub use servo::embedder_traits::EventLoopWaker;
use servo::euclid::Size2D;
use servo::servo_config::opts;
use servo::servo_config::opts::ArgumentParsingResult;
use servo::servo_url::ServoUrl;
use servo::webrender_traits::RenderingContext;
use servo::{self, Servo};
@ -31,6 +29,7 @@ use crate::egl::ohos::InitOpts;
use crate::egl::servo_glue::{
Coordinates, ServoEmbedderCallbacks, ServoGlue, ServoWindowCallbacks,
};
use crate::prefs::{parse_command_line_arguments, ArgumentParsingResult};
/// Initialize Servo. At that point, we need a valid GL context.
/// In the future, this will be done in multiple steps.
@ -46,53 +45,27 @@ pub fn init(
crate::init_crypto();
let resource_dir = PathBuf::from(&options.resource_dir).join("servo");
resources::set(Box::new(ResourceReaderInstance::new(resource_dir)));
let mut args = vec!["servoshell".to_string()];
// It would be nice if `from_cmdline_args()` could accept str slices, to avoid allocations here.
// Then again, this code could and maybe even should be disabled in production builds.
let split_args: Vec<String> = options
.commandline_args
.split("\u{1f}")
.map(|arg| arg.to_string())
.collect();
args.extend(split_args);
let mut args = vec!["servoshell".to_string()];
args.extend(
options
.commandline_args
.split("\u{1f}")
.map(|arg| arg.to_string()),
);
debug!("Servo commandline args: {:?}", args);
let mut opts = getopts::Options::new();
opts.optopt(
"u",
"user-agent",
"Set custom user agent string (or ios / android / desktop for platform default)",
"NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)",
);
opts.optmulti(
"",
"pref",
"A preference to set to enable",
"dom.bluetooth.enabled",
);
opts.optmulti(
"",
"pref",
"A preference to set to disable",
"dom.webgpu.enabled=false",
);
opts.optmulti(
"",
"prefs-file",
"Load in additional prefs from a file.",
"--prefs-file /path/to/prefs.json",
);
let opts_matches = match opts::from_cmdline_args(opts, &args) {
ArgumentParsingResult::ContentProcess(matches, _token) => {
error!("Content Process mode not supported / tested yet on OpenHarmony!");
matches
let (opts, preferences, servoshell_preferences) = match parse_command_line_arguments(args) {
ArgumentParsingResult::ContentProcess(..) => {
unreachable!("OHOS does not have support for multiprocess yet.")
},
ArgumentParsingResult::ChromeProcess(opts, preferences, servoshell_preferences) => {
(opts, preferences, servoshell_preferences)
},
ArgumentParsingResult::ChromeProcess(matches) => matches,
};
crate::prefs::register_user_prefs(&opts_matches);
// Initialize surfman
let connection = Connection::new().or(Err("Failed to create connection"))?;
let adapter = connection
@ -144,11 +117,12 @@ pub fn init(
));
let servo = Servo::new(
opts,
preferences,
rendering_context.clone(),
embedder_callbacks,
window_callbacks.clone(),
// User agent: Mozilla/5.0 (<Phone|PC|Tablet>; HarmonyOS 5.0) bla bla
None,
None, /* user_agent */
CompositeTarget::Window,
);
@ -156,7 +130,7 @@ pub fn init(
rendering_context,
servo,
window_callbacks,
Some(options.resource_dir),
servoshell_preferences,
);
let initial_url = ServoUrl::parse(options.url.as_str())

View file

@ -31,6 +31,7 @@ use servo::webrender_traits::RenderingContext;
use servo::{Servo, TopLevelBrowsingContextId};
use crate::egl::host_trait::HostTrait;
use crate::prefs::ServoShellPreferences;
#[derive(Clone, Debug)]
pub struct Coordinates {
@ -84,7 +85,6 @@ pub struct ServoGlue {
need_present: bool,
callbacks: Rc<ServoWindowCallbacks>,
events: Vec<EmbedderEvent>,
resource_dir: Option<String>,
context_menu_sender: Option<IpcSender<ContextMenuResult>>,
/// List of top-level browsing contexts.
@ -98,6 +98,9 @@ pub struct ServoGlue {
/// The webview that is currently focused.
/// Modified by EmbedderMsg::WebViewFocused and EmbedderMsg::WebViewBlurred.
focused_webview_id: Option<WebViewId>,
/// servoshell specific preferences created during startup of the application.
servoshell_preferences: ServoShellPreferences,
}
#[allow(unused)]
@ -106,7 +109,7 @@ impl ServoGlue {
rendering_context: RenderingContext,
servo: Servo<ServoWindowCallbacks>,
callbacks: Rc<ServoWindowCallbacks>,
resource_dir: Option<String>,
servoshell_preferences: ServoShellPreferences,
) -> Self {
Self {
rendering_context,
@ -115,11 +118,11 @@ impl ServoGlue {
need_present: false,
callbacks,
events: vec![],
resource_dir,
context_menu_sender: None,
webviews: HashMap::default(),
creation_order: vec![],
focused_webview_id: None,
servoshell_preferences,
}
}
@ -173,7 +176,7 @@ impl ServoGlue {
/// Load an URL.
pub fn load_uri(&mut self, url: &str) -> Result<(), &'static str> {
info!("load_uri: {}", url);
crate::parser::location_bar_input_to_url(url)
crate::parser::location_bar_input_to_url(url, &self.servoshell_preferences.searchpage)
.ok_or("Can't parse URL")
.and_then(|url| {
let browser_id = self.get_browser_id()?;