layout: More conservatively replace Stylist's Device (#31857)

Instead of replacing Stylist's device on every reflow, only replace it
when the viewport changes. In addition, preserve the root font size from
the previous reflow fixing an issue where `rem` units were not properly
computed between reflows.

This fixes a bug where fonts that are sized using `rem` units change
size on reload.
This commit is contained in:
Martin Robinson 2024-03-26 16:00:50 +01:00 committed by GitHub
parent b71de92569
commit bf3798bbde
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 95 additions and 61 deletions

View file

@ -379,7 +379,10 @@ impl LayoutThread {
root_flow: RefCell::new(None), root_flow: RefCell::new(None),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
epoch: Cell::new(Epoch(1)), epoch: Cell::new(Epoch(1)),
viewport_size: Size2D::new(Au(0), Au(0)), viewport_size: Size2D::new(
Au::from_f32_px(window_size.initial_viewport.width),
Au::from_f32_px(window_size.initial_viewport.height),
),
webrender_api, webrender_api,
stylist: Stylist::new(device, QuirksMode::NoQuirks), stylist: Stylist::new(device, QuirksMode::NoQuirks),
rw_data: Arc::new(Mutex::new(LayoutThreadData { rw_data: Arc::new(Mutex::new(LayoutThreadData {
@ -936,16 +939,6 @@ impl LayoutThread {
); );
trace!("{:?}", ShowSubtree(root_element.as_node())); trace!("{:?}", ShowSubtree(root_element.as_node()));
let initial_viewport = data.window_size.initial_viewport;
let device_pixel_ratio = data.window_size.device_pixel_ratio;
let old_viewport_size = self.viewport_size;
let current_screen_size = Size2D::new(
Au::from_f32_px(initial_viewport.width),
Au::from_f32_px(initial_viewport.height),
);
let origin = data.origin.clone();
// Calculate the actual viewport as per DEVICE-ADAPT § 6 // Calculate the actual viewport as per DEVICE-ADAPT § 6
// If the entire flow tree is invalid, then it will be reflowed anyhow. // If the entire flow tree is invalid, then it will be reflowed anyhow.
let document_shared_lock = document.style_shared_lock(); let document_shared_lock = document.style_shared_lock();
@ -959,19 +952,7 @@ impl LayoutThread {
}; };
let had_used_viewport_units = self.stylist.device().used_viewport_units(); let had_used_viewport_units = self.stylist.device().used_viewport_units();
let device = Device::new( let viewport_size_changed = self.handle_viewport_change(data.window_size, &guards);
MediaType::screen(),
self.stylist.quirks_mode(),
initial_viewport,
device_pixel_ratio,
);
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = current_screen_size;
let viewport_size_changed = self.viewport_size != old_viewport_size;
if viewport_size_changed && had_used_viewport_units { if viewport_size_changed && had_used_viewport_units {
if let Some(mut data) = root_element.mutate_data() { if let Some(mut data) = root_element.mutate_data() {
data.hint.insert(RestyleHint::recascade_subtree()); data.hint.insert(RestyleHint::recascade_subtree());
@ -1063,7 +1044,7 @@ impl LayoutThread {
let mut layout_context = self.build_layout_context( let mut layout_context = self.build_layout_context(
guards.clone(), guards.clone(),
&map, &map,
origin, data.origin.clone(),
data.animation_timeline_value, data.animation_timeline_value,
&data.animations, &data.animations,
data.stylesheets_changed, data.stylesheets_changed,
@ -1518,6 +1499,42 @@ impl LayoutThread {
}, },
}) })
} }
/// Update layout given a new viewport. Returns true if the viewport changed or false if it didn't.
fn handle_viewport_change(
&mut self,
window_size_data: WindowSizeData,
guards: &StylesheetGuards,
) -> bool {
// If the viewport size and device pixel ratio has not changed, do not make any changes.
let au_viewport_size = Size2D::new(
Au::from_f32_px(window_size_data.initial_viewport.width),
Au::from_f32_px(window_size_data.initial_viewport.height),
);
if self.stylist.device().au_viewport_size() == au_viewport_size &&
self.stylist.device().device_pixel_ratio() == window_size_data.device_pixel_ratio
{
return false;
}
let device = Device::new(
MediaType::screen(),
self.stylist.quirks_mode(),
window_size_data.initial_viewport,
window_size_data.device_pixel_ratio,
);
// Preserve any previously computed root font size.
device.set_root_font_size(self.stylist.device().root_font_size());
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, guards);
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = au_viewport_size;
true
}
} }
impl ProfilerMetadataFactory for LayoutThread { impl ProfilerMetadataFactory for LayoutThread {

View file

@ -72,6 +72,7 @@ use style::dom::{TElement, TNode};
use style::driver; use style::driver;
use style::error_reporting::RustLogReporter; use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL}; use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::{Device, MediaList, MediaType}; use style::media_queries::{Device, MediaList, MediaType};
use style::properties::PropertyId; use style::properties::PropertyId;
use style::selector_parser::SnapshotMap; use style::selector_parser::SnapshotMap;
@ -360,7 +361,10 @@ impl LayoutThread {
fragment_tree: Default::default(), fragment_tree: Default::default(),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
epoch: Cell::new(Epoch(1)), epoch: Cell::new(Epoch(1)),
viewport_size: Size2D::new(Au(0), Au(0)), viewport_size: Size2D::new(
Au::from_f32_px(window_size.initial_viewport.width),
Au::from_f32_px(window_size.initial_viewport.height),
),
webrender_api: webrender_api_sender, webrender_api: webrender_api_sender,
stylist: Stylist::new(device, QuirksMode::NoQuirks), stylist: Stylist::new(device, QuirksMode::NoQuirks),
rw_data: Arc::new(Mutex::new(LayoutThreadData { rw_data: Arc::new(Mutex::new(LayoutThreadData {
@ -628,15 +632,6 @@ impl LayoutThread {
Some(x) => x, Some(x) => x,
}; };
let initial_viewport = data.window_size.initial_viewport;
let device_pixel_ratio = data.window_size.device_pixel_ratio;
let current_screen_size = Size2D::new(
Au::from_f32_px(initial_viewport.width),
Au::from_f32_px(initial_viewport.height),
);
let origin = data.origin.clone();
// Calculate the actual viewport as per DEVICE-ADAPT § 6 // Calculate the actual viewport as per DEVICE-ADAPT § 6
// If the entire flow tree is invalid, then it will be reflowed anyhow. // If the entire flow tree is invalid, then it will be reflowed anyhow.
let document_shared_lock = document.style_shared_lock(); let document_shared_lock = document.style_shared_lock();
@ -649,17 +644,14 @@ impl LayoutThread {
ua_or_user: &ua_or_user_guard, ua_or_user: &ua_or_user_guard,
}; };
let device = Device::new( let had_used_viewport_units = self.stylist.device().used_viewport_units();
MediaType::screen(), let viewport_size_changed = self.handle_viewport_change(data.window_size, &guards);
self.stylist.quirks_mode(), if viewport_size_changed && had_used_viewport_units {
initial_viewport, if let Some(mut data) = root_element.mutate_data() {
device_pixel_ratio, data.hint.insert(RestyleHint::recascade_subtree());
); }
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards); }
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = current_screen_size;
if self.first_reflow.get() { if self.first_reflow.get() {
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets { for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
self.stylist self.stylist
@ -739,7 +731,7 @@ impl LayoutThread {
let mut layout_context = self.build_layout_context( let mut layout_context = self.build_layout_context(
guards.clone(), guards.clone(),
&map, &map,
origin, data.origin.clone(),
data.animation_timeline_value, data.animation_timeline_value,
&data.animations, &data.animations,
data.stylesheets_changed, data.stylesheets_changed,
@ -1158,6 +1150,42 @@ impl LayoutThread {
} }
} }
} }
/// Update layout given a new viewport. Returns true if the viewport changed or false if it didn't.
fn handle_viewport_change(
&mut self,
window_size_data: WindowSizeData,
guards: &StylesheetGuards,
) -> bool {
// If the viewport size and device pixel ratio has not changed, do not make any changes.
let au_viewport_size = Size2D::new(
Au::from_f32_px(window_size_data.initial_viewport.width),
Au::from_f32_px(window_size_data.initial_viewport.height),
);
if self.stylist.device().au_viewport_size() == au_viewport_size &&
self.stylist.device().device_pixel_ratio() == window_size_data.device_pixel_ratio
{
return false;
}
let device = Device::new(
MediaType::screen(),
self.stylist.quirks_mode(),
window_size_data.initial_viewport,
window_size_data.device_pixel_ratio,
);
// Preserve any previously computed root font size.
device.set_root_font_size(self.stylist.device().root_font_size());
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, guards);
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = au_viewport_size;
true
}
} }
impl ProfilerMetadataFactory for LayoutThread { impl ProfilerMetadataFactory for LayoutThread {

View file

@ -81,11 +81,13 @@ impl VirtualMethods for HTMLFontElement {
} }
fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool { fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
if attr.local_name() == &local_name!("color") { if attr.local_name() == &local_name!("color") ||
attr.local_name() == &local_name!("size") ||
attr.local_name() == &local_name!("face")
{
return true; return true;
} }
// FIXME: Should also return true for `size` and `face` changes!
self.super_type() self.super_type()
.unwrap() .unwrap()
.attribute_affects_presentational_hints(attr) .attribute_affects_presentational_hints(attr)

View file

@ -1,6 +1,3 @@
[getComputedStyle-calc-mixed-units-002.html] [getComputedStyle-calc-mixed-units-002.html]
[testing width: calc(5% + 4rem)]
expected: FAIL
[testing width: calc(8lh + 7px)] [testing width: calc(8lh + 7px)]
expected: FAIL expected: FAIL

View file

@ -1,2 +0,0 @@
[rem-root-font-size-restyle-1.html]
expected: FAIL

View file

@ -1,6 +1,3 @@
[getComputedStyle-calc-mixed-units-002.html] [getComputedStyle-calc-mixed-units-002.html]
[testing width: calc(5% + 4rem)]
expected: FAIL
[testing width: calc(8lh + 7px)] [testing width: calc(8lh + 7px)]
expected: FAIL expected: FAIL

View file

@ -1,2 +0,0 @@
[rem-root-font-size-restyle-1.html]
expected: FAIL

View file

@ -1,3 +0,0 @@
[detached_layout.html]
[Detached layout doesn't panic]
expected: FAIL