From 2cdad7461da29371680600e496c633ba779c6ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 10 Jul 2026 16:11:57 -0400 Subject: [PATCH] fix(permissions): show only the site permissions Servo enforces --- .../src/shell/internal_pages/site_settings.rs | 6 ++-- crates/ely_domain/src/site_permission.rs | 19 ++++++++++++ .../src/runtime_permissions_tests.rs | 31 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/crates/ely_app/src/shell/internal_pages/site_settings.rs b/crates/ely_app/src/shell/internal_pages/site_settings.rs index 3c6ce2d..feab362 100644 --- a/crates/ely_app/src/shell/internal_pages/site_settings.rs +++ b/crates/ely_app/src/shell/internal_pages/site_settings.rs @@ -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() } diff --git a/crates/ely_domain/src/site_permission.rs b/crates/ely_domain/src/site_permission.rs index 52a7009..b4240bf 100644 --- a/crates/ely_domain/src/site_permission.rs +++ b/crates/ely_domain/src/site_permission.rs @@ -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 { diff --git a/crates/ely_servo_host/src/runtime_permissions_tests.rs b/crates/ely_servo_host/src/runtime_permissions_tests.rs index 85cd181..918e795 100644 --- a/crates/ely_servo_host/src/runtime_permissions_tests.rs +++ b/crates/ely_servo_host/src/runtime_permissions_tests.rs @@ -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 = [ + 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> { let permissions = PermissionStore::default();