servo/ports/servoshell/desktop/protocols/servo.rs
Josh Matthews 6565d982bd
servoshell: Support runtime preference manipulation (#38159)
These changes add a custom servo:preferences URL that allows modifying
selected preferences at runtime. The goal of this work is to make it
easy to test pages while toggling experimental web platform features,
and support quickly changing the User-Agent header.

Testing: Manually verified that spacex.com loads correctly after
changing the user agent, and that https://polygon.io/ displays grid
elements correctly and no console errors with the experimental prefs
enabled.
Fixes: #35862

<img width="1136" height="880" alt="Screenshot 2025-07-18 at 1 06 23 AM"
src="https://github.com/user-attachments/assets/2d27c321-6ca0-43c3-a347-7bc4b55272df"
/>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-08-30 16:51:58 +00:00

93 lines
2.8 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/. */
//! Loads resources using a mapping from well-known shortcuts to resource: urls.
//! Recognized shortcuts:
//! - servo:default-user-agent
//! - servo:experimental-preferences
//! - servo:newtab
//! - servo:preferences
use std::future::Future;
use std::pin::Pin;
use headers::{ContentType, HeaderMapExt};
use net::fetch::methods::{DoneChannel, FetchContext};
use net::protocols::ProtocolHandler;
use net_traits::ResourceFetchTiming;
use net_traits::request::Request;
use net_traits::response::{Response, ResponseBody};
use servo::config::prefs::UserAgentPlatform;
use crate::desktop::protocols::resource::ResourceProtocolHandler;
use crate::prefs::EXPERIMENTAL_PREFS;
#[derive(Default)]
pub struct ServoProtocolHandler {}
impl ProtocolHandler for ServoProtocolHandler {
fn privileged_paths(&self) -> &'static [&'static str] {
&["preferences"]
}
fn is_fetchable(&self) -> bool {
true
}
fn load(
&self,
request: &mut Request,
done_chan: &mut DoneChannel,
context: &FetchContext,
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let url = request.current_url();
match url.path() {
"newtab" => ResourceProtocolHandler::response_for_path(
request,
done_chan,
context,
"/newtab.html",
),
"preferences" => ResourceProtocolHandler::response_for_path(
request,
done_chan,
context,
"/preferences.html",
),
"experimental-preferences" => {
let pref_list = EXPERIMENTAL_PREFS
.iter()
.map(|pref| format!("\"{pref}\""))
.collect::<Vec<String>>()
.join(",");
json_response(request, format!("[{pref_list}]"))
},
"default-user-agent" => {
let user_agent = UserAgentPlatform::default().to_user_agent_string();
json_response(request, format!("\"{user_agent}\""))
},
_ => Box::pin(std::future::ready(Response::network_internal_error(
"Invalid shortcut",
))),
}
}
}
fn json_response(
request: &Request,
body: String,
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let mut response = Response::new(
request.current_url(),
ResourceFetchTiming::new(request.timing_type()),
);
response.headers.typed_insert(ContentType::json());
*response.body.lock().unwrap() = ResponseBody::Done(body.into_bytes());
Box::pin(std::future::ready(response))
}