From b1fd8ab3f862315b24b18696d1d4a2d9ce93e05b 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, 29 May 2026 13:17:00 -0400 Subject: [PATCH] refactor(servo): extract paint coordination into runtime_paint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `runtime.rs` had grown past the 500-line ceiling enforced by `scripts/audit_source_lines.sh` once the grid-pref comment landed (522 lines). Peel the repaint/present pair — `paint_without_readback`, `paint_without_readback_with_completion`, the private `paint_webview`, and `paint_with_readback` — into a sibling `runtime_paint.rs`, exactly the `paint.rs` boundary the embedding architecture doc prescribes. `runtime_paint` is a child module of `runtime` (declared via `#[path]`, mirroring `runtime_context`), so it keeps access to the private `SoftwareServoHost` fields and the `webview()` / `wait_for_paint_completion` / `read_rendered_frame` helpers without widening any visibility. No behaviour change: public API and call sites are identical. runtime.rs 522 -> 469 lines. Build + clippy clean; full workspace test suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/ely_servo_host/src/runtime.rs | 57 +----------------- crates/ely_servo_host/src/runtime_paint.rs | 68 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 55 deletions(-) create mode 100644 crates/ely_servo_host/src/runtime_paint.rs diff --git a/crates/ely_servo_host/src/runtime.rs b/crates/ely_servo_host/src/runtime.rs index 0102d6f..2a239f6 100644 --- a/crates/ely_servo_host/src/runtime.rs +++ b/crates/ely_servo_host/src/runtime.rs @@ -19,6 +19,8 @@ use servo::{ #[path = "runtime_context.rs"] mod runtime_context; +#[path = "runtime_paint.rs"] +mod runtime_paint; use runtime_context::hidpi_scale_from_factor; pub use runtime_context::{RenderingContextKind, ServoSurfaceSize}; @@ -109,19 +111,6 @@ impl SoftwareServoHost { self.create_webview_in_context(tab_id, profile_id, handles) } - /// Paint and present the current surface without RGBA readback. - pub fn paint_without_readback(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> { - self.paint_without_readback_with_completion(webview_id, true) - } - - pub fn paint_without_readback_with_completion( - &mut self, - webview_id: &WebViewId, - wait_for_completion: bool, - ) -> Result<(), ServoHostError> { - self.paint_webview(webview_id, false, wait_for_completion).map(|_| ()) - } - pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool { let Some(webview) = self.webviews.remove(webview_id) else { return false; @@ -173,48 +162,6 @@ impl SoftwareServoHost { }) } - fn paint_webview( - &mut self, - webview_id: &WebViewId, - capture_frame: bool, - wait_for_completion: bool, - ) -> Result, ServoHostError> { - let rendering_context = self.webview(webview_id)?.rendering_context.clone(); - rendering_context.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?; - rendering_context.prepare_for_rendering(); - // Clear the pending-frame flag before `paint()` so barrier callers observe - // the next Servo frame-ready notification for this paint. - { - let webview = self.webview(webview_id)?; - webview.delegate.mark_frame_presented(); - webview.webview.paint(); - } - if wait_for_completion { - self.wait_for_paint_completion(webview_id); - } - let rendered_frame = if capture_frame { - Some(Self::read_rendered_frame(rendering_context.as_ref())?) - } else { - None - }; - rendering_context.present(); - self.webview(webview_id)?.delegate.mark_frame_presented(); - Ok(rendered_frame) - } - - pub fn paint_with_readback( - &mut self, - webview_id: &WebViewId, - wait_for_completion: bool, - ) -> Result<(), ServoHostError> { - let Some(rendered_frame) = self.paint_webview(webview_id, true, wait_for_completion)? - else { - return Err(ServoHostError::RenderedFrameUnavailable); - }; - self.last_rendered_frame = Some(rendered_frame); - Ok(()) - } - /// Returns the current snapshot and acknowledges metadata-only /// updates without clearing Servo's frame-ready signal. pub fn snapshot_and_mark_metadata_observed( diff --git a/crates/ely_servo_host/src/runtime_paint.rs b/crates/ely_servo_host/src/runtime_paint.rs new file mode 100644 index 0000000..16f4b55 --- /dev/null +++ b/crates/ely_servo_host/src/runtime_paint.rs @@ -0,0 +1,68 @@ +use ely_domain::WebViewId; + +use super::SoftwareServoHost; +use crate::{RenderedFrame, ServoHostError}; + +/// Repaint and present coordination for [`SoftwareServoHost`]. +/// +/// Servo's rendering contract is: `notify_new_frame_ready` flags the +/// session, then a single `WebView::paint` + `RenderingContext::present` +/// pair lands that frame. These methods own that pair (plus the optional +/// RGBA readback used by the software path); the rest of the host +/// lifecycle stays in `runtime.rs`. +impl SoftwareServoHost { + /// Paint and present the current surface without RGBA readback. + pub fn paint_without_readback(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> { + self.paint_without_readback_with_completion(webview_id, true) + } + + pub fn paint_without_readback_with_completion( + &mut self, + webview_id: &WebViewId, + wait_for_completion: bool, + ) -> Result<(), ServoHostError> { + self.paint_webview(webview_id, false, wait_for_completion).map(|_| ()) + } + + fn paint_webview( + &mut self, + webview_id: &WebViewId, + capture_frame: bool, + wait_for_completion: bool, + ) -> Result, ServoHostError> { + let rendering_context = self.webview(webview_id)?.rendering_context.clone(); + rendering_context.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?; + rendering_context.prepare_for_rendering(); + // Clear the pending-frame flag before `paint()` so barrier callers observe + // the next Servo frame-ready notification for this paint. + { + let webview = self.webview(webview_id)?; + webview.delegate.mark_frame_presented(); + webview.webview.paint(); + } + if wait_for_completion { + self.wait_for_paint_completion(webview_id); + } + let rendered_frame = if capture_frame { + Some(Self::read_rendered_frame(rendering_context.as_ref())?) + } else { + None + }; + rendering_context.present(); + self.webview(webview_id)?.delegate.mark_frame_presented(); + Ok(rendered_frame) + } + + pub fn paint_with_readback( + &mut self, + webview_id: &WebViewId, + wait_for_completion: bool, + ) -> Result<(), ServoHostError> { + let Some(rendered_frame) = self.paint_webview(webview_id, true, wait_for_completion)? + else { + return Err(ServoHostError::RenderedFrameUnavailable); + }; + self.last_rendered_frame = Some(rendered_frame); + Ok(()) + } +}