fix(permissions): make profile snapshots authoritative

This commit is contained in:
2026-07-09 21:56:36 -04:00
parent c28ec2bee8
commit b422ac1631
47 changed files with 1918 additions and 403 deletions
+68
View File
@@ -156,6 +156,74 @@ fn live_sidecar_rejects_oversized_frame_dimensions() -> Result<(), Box<dyn Error
Ok(())
}
#[test]
fn live_sidecar_rejects_duplicate_permission_snapshot_entries() -> Result<(), Box<dyn Error>> {
let root = TestDirectory::new()?;
let profile_id = ProfileId::new();
let tab_id = TabId::new();
let mut sidecar = Sidecar::spawn(root.path())?;
let mut ensure = ensure_request(&tab_id, &profile_id, "about:blank");
ensure["site_permissions"] = json!([
{
"origin": "https://example.com",
"feature": "camera",
"state": "allow-always",
"revision": 1,
},
{
"origin": "https://example.com",
"feature": "camera",
"state": "deny-always",
"revision": 2,
},
]);
let response = sidecar.exchange(&ensure)?;
assert!(response.error.as_deref().is_some_and(|error| error.contains("duplicate permission")));
sidecar.shutdown()?;
Ok(())
}
#[test]
fn live_sidecar_accepts_permission_snapshot_lifecycle() -> Result<(), Box<dyn Error>> {
let root = TestDirectory::new()?;
let profile_id = ProfileId::new();
let tab_id = TabId::new();
let mut sidecar = Sidecar::spawn(root.path())?;
let mut ensure = ensure_request(&tab_id, &profile_id, "about:blank");
for (generation, state, revision) in [(1, "allow-once", 1), (2, "transferred-allow-once", 2)] {
ensure["site_permission_generation"] = json!(generation);
ensure["site_permissions"] = json!([{
"origin": "https://example.com",
"feature": "camera",
"state": state,
"revision": revision,
}]);
let response = sidecar.exchange(&ensure)?;
assert!(response.error.is_none(), "state={state} error={:?}", response.error);
}
ensure["site_permission_generation"] = json!(3);
ensure["site_permissions"] = json!([]);
let response = sidecar.exchange(&ensure)?;
assert!(response.error.is_none(), "empty snapshot error={:?}", response.error);
ensure["site_permission_generation"] = json!(1);
ensure["site_permissions"] = json!([{
"origin": "https://example.com",
"feature": "camera",
"state": "allow-once",
"revision": 1,
}]);
let response = sidecar.exchange(&ensure)?;
assert!(response.error.is_none(), "stale snapshot error={:?}", response.error);
sidecar.shutdown()?;
Ok(())
}
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
#[test]
fn hardware_sidecar_transfers_a_real_iosurface_mach_descriptor() -> Result<(), Box<dyn Error>> {
@@ -19,7 +19,7 @@ use serde_json::{Value, json};
pub(super) const WIDTH: u32 = 360;
pub(super) const HEIGHT: u32 = 240;
pub(super) const RESPONSE_TIMEOUT: Duration = Duration::from_secs(20);
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 2;
pub(super) const LIVE_PROTOCOL_VERSION: u32 = 3;
pub(super) const MAX_FRAME_DIMENSION: u32 = 16_384;
const MAX_FRAME_BYTE_COUNT: usize = 256 * 1024 * 1024;
@@ -33,6 +33,7 @@ pub(super) fn ensure_request(tab_id: &TabId, profile_id: &ProfileId, url: &str)
"height": HEIGHT,
"page_zoom_percent": 100,
"device_pixel_ratio": 1.0,
"site_permission_generation": 0,
"site_permissions": [],
})
}
+21 -16
View File
@@ -9,8 +9,9 @@ use std::{
use ely_domain::{ProfileId, SiteOrigin, SitePermissionFeature, TabId, UrlText};
use ely_servo_host::{
HidpiScaleRequest, KeyboardTextRequest, MouseClickRequest, MouseDragRequest, NavigationRequest,
PageZoomRequest, PermissionDecision, PermissionRequest, ResizeRequest, ScrollRequest,
ServoHost, ServoHostError, ServoSurfaceSize, SoftwareServoHost, TouchTapRequest, WebViewState,
PageZoomRequest, PermissionDecision, PermissionSnapshotEntry, PermissionSnapshotRequest,
PermissionSnapshotState, ResizeRequest, ScrollRequest, ServoHost, ServoHostError,
ServoSurfaceSize, SoftwareServoHost, TouchTapRequest, WebViewState,
};
const MINIMUM_CONTENT_PIXELS: u64 = 1_000;
@@ -202,25 +203,29 @@ fn exercise_real_servo_webview_lifecycle() -> Result<(), Box<dyn Error>> {
assert_eq!(snapshot.profile_id(), &profile_id);
assert_eq!(snapshot.state(), &WebViewState::Created);
host.set_permission(
PermissionRequest {
webview_id: webview_id.clone(),
profile_id: profile_id.clone(),
host.replace_permissions(PermissionSnapshotRequest {
webview_id: webview_id.clone(),
profile_id: profile_id.clone(),
generation: 1,
entries: vec![PermissionSnapshotEntry {
origin: SiteOrigin::parse("https://example.com")?,
feature: SitePermissionFeature::Camera,
},
PermissionDecision::AllowOnce,
)?;
state: PermissionSnapshotState::Decision(PermissionDecision::AllowOnce),
revision: 1,
}],
})?;
let other_profile_id = ProfileId::new();
let mismatch = host.set_permission(
PermissionRequest {
webview_id: webview_id.clone(),
profile_id: other_profile_id.clone(),
let mismatch = host.replace_permissions(PermissionSnapshotRequest {
webview_id: webview_id.clone(),
profile_id: other_profile_id.clone(),
generation: 2,
entries: vec![PermissionSnapshotEntry {
origin: SiteOrigin::parse("https://example.com")?,
feature: SitePermissionFeature::Camera,
},
PermissionDecision::AllowAlways,
);
state: PermissionSnapshotState::Decision(PermissionDecision::AllowAlways),
revision: 1,
}],
});
assert!(
matches!(
mismatch,