Add Servo site compatibility frame smoke

This commit is contained in:
2026-05-08 12:08:57 -04:00
parent b4355d0f82
commit 5f0773a043
5 changed files with 223 additions and 20 deletions
+3
View File
@@ -20,4 +20,7 @@ pub enum ServoHostError {
#[error("servo rendering context could not be made current")]
RenderingContextNotCurrent,
#[error("servo rendered frame is unavailable")]
RenderedFrameUnavailable,
}
+114
View File
@@ -11,6 +11,118 @@ pub enum WebViewState {
Crashed,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RenderedFrameSummary {
width: u32,
height: u32,
opaque_pixel_count: u64,
non_white_pixel_count: u64,
sample_hash: u64,
}
impl RenderedFrameSummary {
#[must_use]
pub fn from_rgba_bytes(width: u32, height: u32, rgba_bytes: &[u8]) -> Self {
let mut opaque_pixel_count = 0;
let mut non_white_pixel_count = 0;
let mut sample_hash = 0xcbf29ce484222325_u64;
for (index, pixel) in rgba_bytes.chunks_exact(4).enumerate() {
let [red, green, blue, alpha] = [pixel[0], pixel[1], pixel[2], pixel[3]];
if alpha > 0 {
opaque_pixel_count += 1;
}
if alpha > 0 && (red < 245 || green < 245 || blue < 245) {
non_white_pixel_count += 1;
}
if index % 97 == 0 {
for byte in pixel {
sample_hash ^= u64::from(*byte);
sample_hash = sample_hash.wrapping_mul(0x100000001b3);
}
}
}
Self { width, height, opaque_pixel_count, non_white_pixel_count, sample_hash }
}
#[must_use]
pub fn width(&self) -> u32 {
self.width
}
#[must_use]
pub fn height(&self) -> u32 {
self.height
}
#[must_use]
pub fn opaque_pixel_count(&self) -> u64 {
self.opaque_pixel_count
}
#[must_use]
pub fn non_white_pixel_count(&self) -> u64 {
self.non_white_pixel_count
}
#[must_use]
pub fn sample_hash(&self) -> u64 {
self.sample_hash
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RenderedFrame {
width: u32,
height: u32,
rgba_bytes: Vec<u8>,
summary: RenderedFrameSummary,
}
impl RenderedFrame {
#[must_use]
pub fn from_rgba_bytes(width: u32, height: u32, rgba_bytes: Vec<u8>) -> Self {
let summary = RenderedFrameSummary::from_rgba_bytes(width, height, &rgba_bytes);
Self { width, height, rgba_bytes, summary }
}
#[must_use]
pub fn width(&self) -> u32 {
self.width
}
#[must_use]
pub fn height(&self) -> u32 {
self.height
}
#[must_use]
pub fn rgba_bytes(&self) -> &[u8] {
&self.rgba_bytes
}
#[must_use]
pub fn summary(&self) -> &RenderedFrameSummary {
&self.summary
}
#[must_use]
pub fn opaque_pixel_count(&self) -> u64 {
self.summary.opaque_pixel_count()
}
#[must_use]
pub fn non_white_pixel_count(&self) -> u64 {
self.summary.non_white_pixel_count()
}
#[must_use]
pub fn sample_hash(&self) -> u64 {
self.summary.sample_hash()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WebViewSnapshot {
webview_id: WebViewId,
@@ -116,4 +228,6 @@ pub trait ServoHost {
fn tick(&mut self) -> bool;
fn paint(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError>;
fn last_rendered_frame(&self) -> Result<RenderedFrame, ServoHostError>;
}
+2 -2
View File
@@ -5,8 +5,8 @@ mod runtime;
pub use error::ServoHostError;
pub use host::{
NavigationRequest, PermissionDecision, PermissionRequest, ServoHost, WebViewSnapshot,
WebViewState,
NavigationRequest, PermissionDecision, PermissionRequest, RenderedFrame, RenderedFrameSummary,
ServoHost, WebViewSnapshot, WebViewState,
};
#[cfg(feature = "servo-engine")]
pub use runtime::{ServoSurfaceSize, SoftwareServoHost};
+35 -7
View File
@@ -11,14 +11,14 @@ use std::{
use dpi::PhysicalSize;
use ely_domain::{ProfileId, TabId, WebViewId};
use servo::{
EventLoopWaker, LoadStatus, RenderingContext, Servo, ServoBuilder, WebView, WebViewBuilder,
WebViewDelegate,
DeviceIntPoint, DeviceIntRect, DeviceIntSize, EventLoopWaker, LoadStatus, RenderingContext,
Servo, ServoBuilder, WebView, WebViewBuilder, WebViewDelegate,
};
use url::Url;
use crate::{
NavigationRequest, PermissionDecision, PermissionRequest, ServoHost, ServoHostError,
WebViewSnapshot, WebViewState,
NavigationRequest, PermissionDecision, PermissionRequest, RenderedFrame, ServoHost,
ServoHostError, WebViewSnapshot, WebViewState,
};
static SERVO_RUNTIME_STARTED: AtomicBool = AtomicBool::new(false);
@@ -46,6 +46,7 @@ pub struct SoftwareServoHost {
webviews: HashMap<WebViewId, HostWebView>,
permissions: PermissionStore,
wake_requested: Arc<AtomicBool>,
last_rendered_frame: Option<RenderedFrame>,
}
impl SoftwareServoHost {
@@ -82,6 +83,7 @@ impl SoftwareServoHost {
webviews: HashMap::new(),
permissions: Rc::new(RefCell::new(HashMap::new())),
wake_requested,
last_rendered_frame: None,
})
}
}
@@ -171,16 +173,24 @@ impl ServoHost for SoftwareServoHost {
}
fn paint(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> {
let webview = self.webview(webview_id)?;
self.rendering_context
.make_current()
.map_err(|_| ServoHostError::RenderingContextNotCurrent)?;
self.rendering_context.prepare_for_rendering();
webview.webview.paint();
{
let webview = self.webview(webview_id)?;
webview.webview.paint();
}
let rendered_frame = self.read_rendered_frame()?;
self.rendering_context.present();
webview.delegate.mark_frame_presented();
self.webview(webview_id)?.delegate.mark_frame_presented();
self.last_rendered_frame = Some(rendered_frame);
Ok(())
}
fn last_rendered_frame(&self) -> Result<RenderedFrame, ServoHostError> {
self.last_rendered_frame.clone().ok_or(ServoHostError::RenderedFrameUnavailable)
}
}
impl SoftwareServoHost {
@@ -189,6 +199,24 @@ impl SoftwareServoHost {
.get(webview_id)
.ok_or_else(|| ServoHostError::WebViewNotFound { id: webview_id.clone() })
}
fn read_rendered_frame(&self) -> Result<RenderedFrame, ServoHostError> {
let size = self.rendering_context.size();
let width =
i32::try_from(size.width).map_err(|_| ServoHostError::RenderedFrameUnavailable)?;
let height =
i32::try_from(size.height).map_err(|_| ServoHostError::RenderedFrameUnavailable)?;
let frame_rect = DeviceIntRect::from_origin_and_size(
DeviceIntPoint::new(0, 0),
DeviceIntSize::new(width, height),
);
let image = self
.rendering_context
.read_to_image(frame_rect)
.ok_or(ServoHostError::RenderedFrameUnavailable)?;
Ok(RenderedFrame::from_rgba_bytes(size.width, size.height, image.into_raw()))
}
}
struct HostWebView {