Speed up retained rendering and honor PDF backgrounds
This commit is contained in:
1 parent
222b50e480
commit
ede14b7740
9 files changed
+1385
-92
No files matched your search
@@ -264,7 +264,7 @@ impl ObscuraJsRuntime {
|
||||
#[cfg(feature = "render")]
|
||||
{
|
||||
gs.prepared_render = None;
|
||||
gs.pending_attribute_style_mutations.clear();
|
||||
gs.pending_style_mutations.clear();
|
||||
gs.render_resources = obscura_render::RenderResourceCache::default();
|
||||
gs.stylesheet_cache = obscura_render::StylesheetCache::default();
|
||||
gs.dynamic_fonts.clear();
|
||||
@@ -285,7 +285,7 @@ impl ObscuraJsRuntime {
|
||||
// present. Keep already-fetched absolute bytes, but rebuild
|
||||
// candidate selection/layout against the new base.
|
||||
state.prepared_render = None;
|
||||
state.pending_attribute_style_mutations.clear();
|
||||
state.pending_style_mutations.clear();
|
||||
state.resolved_scroll = None;
|
||||
}
|
||||
}
|
||||
@@ -396,7 +396,7 @@ impl ObscuraJsRuntime {
|
||||
if state.viewport != viewport {
|
||||
state.viewport = viewport;
|
||||
state.prepared_render = None;
|
||||
state.pending_attribute_style_mutations.clear();
|
||||
state.pending_style_mutations.clear();
|
||||
state.resolved_scroll = None;
|
||||
}
|
||||
}
|
||||
@@ -465,6 +465,17 @@ impl ObscuraJsRuntime {
|
||||
pub fn screenshot_prepared_region(
|
||||
&self,
|
||||
region: obscura_render::CaptureRegion,
|
||||
) -> Result<Vec<u8>, obscura_render::CaptureError> {
|
||||
self.screenshot_prepared_region_with_backgrounds(region, true)
|
||||
}
|
||||
|
||||
/// Capture a document-space rectangle with the PDF print-background
|
||||
/// policy without mutating the page DOM or retained geometry.
|
||||
#[cfg(feature = "render")]
|
||||
pub fn screenshot_prepared_region_with_backgrounds(
|
||||
&self,
|
||||
region: obscura_render::CaptureRegion,
|
||||
paint_backgrounds: bool,
|
||||
) -> Result<Vec<u8>, obscura_render::CaptureError> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
with_sync_render_loading_disabled(&mut state, |state| {
|
||||
@@ -479,7 +490,7 @@ impl ObscuraJsRuntime {
|
||||
let (_, scroll) = resolved_scroll
|
||||
.as_ref()
|
||||
.ok_or(obscura_render::CaptureError::PaintFailed)?;
|
||||
obscura_render::screenshot_prepared_region_with_scroll(
|
||||
obscura_render::screenshot_prepared_region_with_scroll_and_backgrounds(
|
||||
dom.as_ref()
|
||||
.ok_or(obscura_render::CaptureError::PaintFailed)?,
|
||||
prepared_render
|
||||
@@ -488,6 +499,7 @@ impl ObscuraJsRuntime {
|
||||
render_resources,
|
||||
scroll,
|
||||
region,
|
||||
paint_backgrounds,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -557,7 +569,7 @@ impl ObscuraJsRuntime {
|
||||
Some(bytes) => {
|
||||
state.render_resources.seed(url, bytes);
|
||||
state.prepared_render = None;
|
||||
state.pending_attribute_style_mutations.clear();
|
||||
state.pending_style_mutations.clear();
|
||||
state.resolved_scroll = None;
|
||||
}
|
||||
None => state.render_resources.seed_missing(url),
|
||||
@@ -1576,7 +1588,7 @@ impl ObscuraJsRuntime {
|
||||
#[cfg(feature = "render")]
|
||||
{
|
||||
state.prepared_render = None;
|
||||
state.pending_attribute_style_mutations.clear();
|
||||
state.pending_style_mutations.clear();
|
||||
state.render_resources = obscura_render::RenderResourceCache::default();
|
||||
state.stylesheet_cache = obscura_render::StylesheetCache::default();
|
||||
state.dynamic_fonts.clear();
|
||||
@@ -6141,10 +6153,19 @@ mod tests {
|
||||
"document.getElementById('box').setAttribute('style', 'height:60px;width:40px')",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
rt.state.borrow().prepared_render.is_none(),
|
||||
"a changed attribute on a connected element must invalidate layout"
|
||||
);
|
||||
{
|
||||
let state = rt.state.borrow();
|
||||
assert!(
|
||||
state.prepared_render.is_some(),
|
||||
"a retained inline-style change must keep the prior style maps until flush"
|
||||
);
|
||||
assert!(matches!(
|
||||
state.pending_style_mutations.as_slice(),
|
||||
[obscura_render::RetainedStyleMutation::Attribute(
|
||||
obscura_render::AttributeStyleMutation { name, .. }
|
||||
)] if name == "style"
|
||||
));
|
||||
}
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('box').getBoundingClientRect().height")
|
||||
.unwrap()
|
||||
@@ -6189,7 +6210,7 @@ mod tests {
|
||||
{
|
||||
let state = rt.state.borrow();
|
||||
assert!(state.prepared_render.is_some());
|
||||
assert_eq!(state.pending_attribute_style_mutations.len(), 1);
|
||||
assert_eq!(state.pending_style_mutations.len(), 1);
|
||||
}
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('box').getBoundingClientRect().width")
|
||||
@@ -6235,6 +6256,98 @@ mod tests {
|
||||
assert_eq!(state.stylesheet_cache.hit_count(), 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[test]
|
||||
fn connected_tree_mutations_queue_retained_styles_until_geometry_flush() {
|
||||
let dom = parse_html(
|
||||
r#"<html style="margin:0"><head><style>
|
||||
.item{display:block;width:40px;height:12px}
|
||||
.item:nth-child(2){width:80px}
|
||||
</style></head><body style="margin:0">
|
||||
<main id="list"><div id="first" class="item"></div></main>
|
||||
</body></html>"#,
|
||||
);
|
||||
let mut rt = ObscuraJsRuntime::new();
|
||||
rt.set_dom(dom);
|
||||
rt.set_viewport(200.0, 100.0);
|
||||
rt.run_page_init();
|
||||
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('first').getBoundingClientRect().width")
|
||||
.unwrap()
|
||||
.as_f64(),
|
||||
Some(40.0)
|
||||
);
|
||||
rt.evaluate(
|
||||
"const added=document.createElement('div');added.id='added';added.className='item';document.getElementById('list').appendChild(added)",
|
||||
)
|
||||
.unwrap();
|
||||
{
|
||||
let state = rt.state.borrow();
|
||||
assert!(state.prepared_render.is_some());
|
||||
assert!(matches!(
|
||||
state.pending_style_mutations.as_slice(),
|
||||
[obscura_render::RetainedStyleMutation::Tree(
|
||||
obscura_render::TreeStyleMutation::Insert { .. }
|
||||
)]
|
||||
));
|
||||
}
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('added').getBoundingClientRect().width")
|
||||
.unwrap()
|
||||
.as_f64(),
|
||||
Some(80.0)
|
||||
);
|
||||
assert!(rt.state.borrow().pending_style_mutations.is_empty());
|
||||
|
||||
rt.evaluate("document.getElementById('added').style.width='65px'")
|
||||
.unwrap();
|
||||
{
|
||||
let state = rt.state.borrow();
|
||||
assert!(state.prepared_render.is_some());
|
||||
assert!(matches!(
|
||||
state.pending_style_mutations.as_slice(),
|
||||
[obscura_render::RetainedStyleMutation::Attribute(
|
||||
obscura_render::AttributeStyleMutation { name, .. }
|
||||
)] if name == "style"
|
||||
));
|
||||
}
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('added').getBoundingClientRect().width")
|
||||
.unwrap()
|
||||
.as_f64(),
|
||||
Some(65.0)
|
||||
);
|
||||
assert_eq!(
|
||||
rt.evaluate("(function(){const added=document.getElementById('added');added.style.width='';return added.getBoundingClientRect().width})()")
|
||||
.unwrap()
|
||||
.as_f64(),
|
||||
Some(80.0)
|
||||
);
|
||||
|
||||
rt.evaluate(
|
||||
"document.getElementById('list').removeChild(document.getElementById('first'))",
|
||||
)
|
||||
.unwrap();
|
||||
{
|
||||
let state = rt.state.borrow();
|
||||
assert!(state.prepared_render.is_some());
|
||||
assert!(matches!(
|
||||
state.pending_style_mutations.as_slice(),
|
||||
[obscura_render::RetainedStyleMutation::Tree(
|
||||
obscura_render::TreeStyleMutation::Remove { .. }
|
||||
)]
|
||||
));
|
||||
}
|
||||
assert_eq!(
|
||||
rt.evaluate("document.getElementById('added').getBoundingClientRect().width")
|
||||
.unwrap()
|
||||
.as_f64(),
|
||||
Some(40.0)
|
||||
);
|
||||
assert!(rt.state.borrow().pending_style_mutations.is_empty());
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[test]
|
||||
fn root_overflow_clip_preserves_cssom_scroll_range() {
|
||||
|
||||
Reference in new issue
Block a user