Validate Servo hardware frame surfaces
This commit is contained in:
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use core_video::pixel_buffer::CVPixelBuffer;
|
use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA};
|
||||||
use gpui::RenderImage;
|
use gpui::RenderImage;
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -109,6 +109,11 @@ impl WebSurfaceFrame {
|
|||||||
return Err(WebSurfaceError::MissingRenderablePayload);
|
return Err(WebSurfaceError::MissingRenderablePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
if let Some(pixel_buffer) = parts.pixel_buffer.as_ref() {
|
||||||
|
validate_hardware_pixel_buffer(pixel_buffer, parts.width, parts.height)?;
|
||||||
|
}
|
||||||
|
|
||||||
let image = if parts.rgba_bytes.is_empty() {
|
let image = if parts.rgba_bytes.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -249,6 +254,44 @@ pub(super) enum WebSurfaceError {
|
|||||||
InvalidFrameBuffer { width: u32, height: u32 },
|
InvalidFrameBuffer { width: u32, height: u32 },
|
||||||
#[error("servo live frame did not include a software image or hardware IOSurface")]
|
#[error("servo live frame did not include a software image or hardware IOSurface")]
|
||||||
MissingRenderablePayload,
|
MissingRenderablePayload,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[error(
|
||||||
|
"servo hardware surface size {actual_width}x{actual_height} did not match frame report {expected_width}x{expected_height}"
|
||||||
|
)]
|
||||||
|
HardwareSurfaceSizeMismatch {
|
||||||
|
expected_width: u32,
|
||||||
|
expected_height: u32,
|
||||||
|
actual_width: usize,
|
||||||
|
actual_height: usize,
|
||||||
|
},
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[error("servo hardware surface pixel format 0x{actual:x} is unsupported; expected 32BGRA")]
|
||||||
|
UnsupportedHardwareSurfaceFormat { actual: u32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn validate_hardware_pixel_buffer(
|
||||||
|
pixel_buffer: &CVPixelBuffer,
|
||||||
|
expected_width: u32,
|
||||||
|
expected_height: u32,
|
||||||
|
) -> Result<(), WebSurfaceError> {
|
||||||
|
let actual_width = pixel_buffer.get_width();
|
||||||
|
let actual_height = pixel_buffer.get_height();
|
||||||
|
if actual_width != expected_width as usize || actual_height != expected_height as usize {
|
||||||
|
return Err(WebSurfaceError::HardwareSurfaceSizeMismatch {
|
||||||
|
expected_width,
|
||||||
|
expected_height,
|
||||||
|
actual_width,
|
||||||
|
actual_height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual_format = pixel_buffer.get_pixel_format();
|
||||||
|
if actual_format != kCVPixelFormatType_32BGRA {
|
||||||
|
return Err(WebSurfaceError::UnsupportedHardwareSurfaceFormat { actual: actual_format });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swap byte 0 and byte 2 of every 4-byte pixel, converting Servo's
|
/// Swap byte 0 and byte 2 of every 4-byte pixel, converting Servo's
|
||||||
|
|||||||
@@ -432,6 +432,64 @@ fn hardware_live_frame_with_pixel_buffer_skips_software_image() -> Result<(), St
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[test]
|
||||||
|
fn hardware_live_frame_rejects_mismatched_surface_size() -> Result<(), String> {
|
||||||
|
use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_32BGRA};
|
||||||
|
|
||||||
|
use crate::services::servo_live::ServoLiveFrame;
|
||||||
|
use crate::shell::web_surface_frame::WebSurfaceFrame;
|
||||||
|
use crate::shell::web_surface_geometry::WebSurfaceScrollOffset;
|
||||||
|
|
||||||
|
let pixel_buffer = CVPixelBuffer::new(kCVPixelFormatType_32BGRA, 2, 1, None)
|
||||||
|
.map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?;
|
||||||
|
let live = ServoLiveFrame::for_test_with_pixel_buffer(1, 1, pixel_buffer);
|
||||||
|
let result = WebSurfaceFrame::from_live_frame(
|
||||||
|
"https://example.com/".to_string(),
|
||||||
|
WebSurfaceScrollOffset::default(),
|
||||||
|
100,
|
||||||
|
live,
|
||||||
|
);
|
||||||
|
|
||||||
|
let error = match result {
|
||||||
|
Ok(_) => return Err("mismatched hardware surface reached Ready state".to_string()),
|
||||||
|
Err(error) => error,
|
||||||
|
};
|
||||||
|
assert_eq!(error.to_string(), "servo hardware surface size 2x1 did not match frame report 1x1",);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[test]
|
||||||
|
fn hardware_live_frame_rejects_unsupported_surface_format() -> Result<(), String> {
|
||||||
|
use core_video::pixel_buffer::{CVPixelBuffer, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange};
|
||||||
|
|
||||||
|
use crate::services::servo_live::ServoLiveFrame;
|
||||||
|
use crate::shell::web_surface_frame::WebSurfaceFrame;
|
||||||
|
use crate::shell::web_surface_geometry::WebSurfaceScrollOffset;
|
||||||
|
|
||||||
|
let pixel_buffer =
|
||||||
|
CVPixelBuffer::new(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, 2, 2, None)
|
||||||
|
.map_err(|status| format!("CVPixelBufferCreate returned status {status}"))?;
|
||||||
|
let live = ServoLiveFrame::for_test_with_pixel_buffer(2, 2, pixel_buffer);
|
||||||
|
let result = WebSurfaceFrame::from_live_frame(
|
||||||
|
"https://example.com/".to_string(),
|
||||||
|
WebSurfaceScrollOffset::default(),
|
||||||
|
100,
|
||||||
|
live,
|
||||||
|
);
|
||||||
|
|
||||||
|
let error = match result {
|
||||||
|
Ok(_) => return Err("unsupported hardware surface format reached Ready state".to_string()),
|
||||||
|
Err(error) => error,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
error.to_string(),
|
||||||
|
"servo hardware surface pixel format 0x34323066 is unsupported; expected 32BGRA",
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn web_bounds() -> Bounds<gpui::Pixels> {
|
fn web_bounds() -> Bounds<gpui::Pixels> {
|
||||||
Bounds::new(point(px(0.0), px(0.0)), size(px(640.0), px(480.0)))
|
Bounds::new(point(px(0.0), px(0.0)), size(px(640.0), px(480.0)))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user