fix(permissions): show only the site permissions Servo enforces

This commit is contained in:
2026-07-10 16:11:57 -04:00
parent d271670906
commit 2cdad7461d
3 changed files with 53 additions and 3 deletions
@@ -84,9 +84,9 @@ fn render_site_permission_rows(
div().text_xs().font_semibold().text_color(rgb(colors::muted())).child("Permissions"),
)
.child(div().flex_1().min_h_0().flex().flex_col().overflow_y_scrollbar().children(
SitePermissionFeature::all().iter().copied().enumerate().map(|(index, feature)| {
render_site_permission_row(snapshot, origin, index, feature, cx)
}),
SitePermissionFeature::enforced().iter().copied().enumerate().map(
|(index, feature)| render_site_permission_row(snapshot, origin, index, feature, cx),
),
))
.into_any_element()
}
+19
View File
@@ -124,6 +124,25 @@ impl SitePermissionFeature {
]
}
/// The features ELY actually enforces: each is driven by a
/// `servo::PermissionFeature` the host maps in
/// `ely_servo_host::runtime_permissions`. The other `all()` variants
/// are kept for stored and synced records but are not offered as
/// controllable toggles, because nothing consults them yet — showing
/// them would be a placebo. A drift guard
/// (`enforced_features_match_the_servo_mapping`) keeps this in step
/// with the engine.
#[must_use]
pub fn enforced() -> &'static [Self] {
&[
Self::Camera,
Self::Microphone,
Self::Location,
Self::Notifications,
Self::StoragePersistence,
]
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
@@ -18,6 +18,37 @@ fn keeps_disabled_servo_permissions_out_of_site_settings() {
}
}
/// The site-settings toggles come from `SitePermissionFeature::enforced()`.
/// This pins that list to what the Servo permission mapping actually
/// honors, so the UI can never drift back into offering placebo toggles.
/// (The mapping match is exhaustive over `servo::PermissionFeature`, so a
/// new upstream variant already fails to compile until it is mapped.)
#[test]
fn enforced_features_match_the_servo_mapping() {
let mapped: Vec<SitePermissionFeature> = [
servo::PermissionFeature::Camera,
servo::PermissionFeature::Microphone,
servo::PermissionFeature::Geolocation,
servo::PermissionFeature::Notifications,
servo::PermissionFeature::PersistentStorage,
]
.into_iter()
.filter_map(site_permission_feature_for_servo)
.collect();
assert_eq!(mapped.as_slice(), SitePermissionFeature::enforced());
for feature in SitePermissionFeature::enforced() {
assert!(SitePermissionFeature::all().contains(feature));
}
for placebo in [
SitePermissionFeature::ClipboardRead,
SitePermissionFeature::WebUsb,
SitePermissionFeature::Popups,
] {
assert!(!SitePermissionFeature::enforced().contains(&placebo));
}
}
#[test]
fn allow_once_is_consumed_after_one_matching_request() -> Result<(), Box<dyn std::error::Error>> {
let permissions = PermissionStore::default();