render: unify responsive geometry and inline gradients

This commit is contained in:
SGavrl committed 2026-07-26 11:51:44 +00:00
1 parent 29d0bce3f5
commit 3618549842
16 files changed
+587 -102

No files matched your search

@@ -0,0 +1,98 @@
use serde_json::{json, Value};
use crate::dispatch::CdpContext;
pub async fn handle(
method: &str,
params: &Value,
ctx: &mut CdpContext,
session_id: &Option<String>,
) -> Result<Value, String> {
match method {
"setDeviceMetricsOverride" => {
let width = params
.get("width")
.and_then(Value::as_f64)
.ok_or("Emulation.setDeviceMetricsOverride requires width")?;
let height = params
.get("height")
.and_then(Value::as_f64)
.ok_or("Emulation.setDeviceMetricsOverride requires height")?;
if !width.is_finite() || !height.is_finite() || width <= 0.0 || height <= 0.0 {
return Err(
"Emulation.setDeviceMetricsOverride requires positive finite dimensions"
.to_string(),
);
}
let page = ctx
.get_session_page_mut(session_id)
.ok_or("No page for session")?;
page.set_viewport((width as f32, height as f32));
Ok(json!({}))
}
"clearDeviceMetricsOverride" => {
let page = ctx
.get_session_page_mut(session_id)
.ok_or("No page for session")?;
page.set_viewport((1280.0, 720.0));
Ok(json!({}))
}
// Touch emulation does not affect layout yet, but acknowledging it is
// compatible with clients that pair it with a metrics override.
"setTouchEmulationEnabled" => Ok(json!({})),
_ => Ok(json!({})),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn device_metrics_override_updates_page_and_window_viewport() {
let mut ctx = CdpContext::new();
let page_id = ctx.create_page();
let session_id = Some("viewport-session".to_string());
ctx.sessions
.insert(session_id.clone().unwrap(), page_id);
handle(
"setDeviceMetricsOverride",
&json!({"width": 1024, "height": 768, "deviceScaleFactor": 1}),
&mut ctx,
&session_id,
)
.await
.expect("metrics override");
let page = ctx
.get_session_page_mut(&session_id)
.expect("page for session");
assert_eq!(page.viewport, (1024.0, 768.0));
assert_eq!(
page.evaluate(
"return [innerWidth, innerHeight, visualViewport.width,\
visualViewport.height];"
),
json!([1024, 768, 1024, 768])
);
}
#[tokio::test]
async fn device_metrics_override_rejects_zero_dimensions() {
let mut ctx = CdpContext::new();
let page_id = ctx.create_page();
let session_id = Some("viewport-session".to_string());
ctx.sessions
.insert(session_id.clone().unwrap(), page_id);
let error = handle(
"setDeviceMetricsOverride",
&json!({"width": 0, "height": 768}),
&mut ctx,
&session_id,
)
.await
.expect_err("zero width must fail");
assert!(error.contains("positive finite dimensions"));
}
}
+1
View File
@@ -11,3 +11,4 @@ pub mod input;
pub mod storage;
pub mod accessibility;
pub mod lp;
pub mod emulation;
+7 -8
View File
@@ -400,13 +400,12 @@ pub async fn handle(
// configure; ack it so clients that set it do not warn (issue #340).
"setDownloadBehavior" => Ok(json!({})),
"getLayoutMetrics" => {
// Obscura has no visual layout engine, so we return a fixed
// 1280x720 viewport (Chrome's default) and try to derive the
// content height from document.documentElement.scrollHeight.
// Playwright calls this before every page.screenshot() and
// would otherwise fail with "Unknown Page method".
let width = 1280.0_f64;
let height = 720.0_f64;
// Playwright calls this before every page.screenshot(). Report the
// same live CSS viewport that responsive page code and paint use.
let (width, height) = ctx
.get_session_page(session_id)
.map(|page| (page.viewport.0 as f64, page.viewport.1 as f64))
.unwrap_or((1280.0, 720.0));
let content_height = ctx
.get_session_page_mut(session_id)
.map(|p| p.evaluate("document.documentElement && document.documentElement.scrollHeight"))
@@ -528,7 +527,7 @@ pub async fn handle(
#[cfg(feature = "render")]
{
let page = ctx.get_session_page(session_id).ok_or("No page for session")?;
match page.screenshot((1280.0, 720.0)) {
match page.screenshot(page.viewport) {
Some(png) => {
use base64::Engine as _;
let data = base64::engine::general_purpose::STANDARD.encode(&png);