mirror of
https://github.com/lexmount/moli.git
synced 2026-11-13 08:08:10 +00:00
perf(fonts): batch membership retirement and update priorities in place
Clear and CSS manifest replacement rebuilt the physical font collection once per removed face; changing matching order also re-registered every font. Retire each Document membership change in one batch, rebuild at most once for actual removals, and update candidate priorities/caches in place for order or membership-only changes. Byte/descriptor replacements still rebuild to preserve other sources with identical Fontique attributes. Validation: cargo fmt --all; strict all-feature workspace Clippy; cargo nextest run --no-fail-fast (20683 tests run: 20683 passed (9 slow), 19 skipped).
This commit is contained in:
+150
-80
@@ -863,6 +863,25 @@ pub struct DocumentLayoutServices {
|
||||
web_font_order: BTreeMap<String, (bool, usize)>,
|
||||
has_font_face_set_order: bool,
|
||||
pub(crate) text_layout_passes: u64,
|
||||
#[cfg(test)]
|
||||
font_collection_rebuilds: usize,
|
||||
}
|
||||
|
||||
/// Restores a temporary native pass's candidate order, including unwinding.
|
||||
/// Physical Fontique families and both text contexts remain untouched.
|
||||
struct ScopedWebFontOrder<'a> {
|
||||
services: &'a mut DocumentLayoutServices,
|
||||
previous: BTreeMap<String, (bool, usize)>,
|
||||
}
|
||||
|
||||
impl Drop for ScopedWebFontOrder<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.services.web_font_order = std::mem::take(&mut self.previous);
|
||||
self.services.refresh_web_font_priorities();
|
||||
if let Some(parley) = self.services.parley.as_mut() {
|
||||
parley.invalidate_font_selection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DocumentLayoutServices {
|
||||
@@ -882,6 +901,8 @@ impl DocumentLayoutServices {
|
||||
web_font_order: BTreeMap::new(),
|
||||
has_font_face_set_order: false,
|
||||
text_layout_passes: 0,
|
||||
#[cfg(test)]
|
||||
font_collection_rebuilds: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,6 +916,8 @@ impl DocumentLayoutServices {
|
||||
web_font_order: BTreeMap::new(),
|
||||
has_font_face_set_order: false,
|
||||
text_layout_passes: 0,
|
||||
#[cfg(test)]
|
||||
font_collection_rebuilds: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,37 +986,54 @@ impl DocumentLayoutServices {
|
||||
.into_iter()
|
||||
.map(|(slot, _)| slot.clone())
|
||||
.collect::<Vec<_>>();
|
||||
if current == previous {
|
||||
// Unloaded members can shift indices without changing the current
|
||||
// registered order. Future incremental arrivals use the new indices.
|
||||
if let Some(parley) = self.parley.as_mut() {
|
||||
for family in parley.web_font_families.values_mut() {
|
||||
for group in &mut family.groups {
|
||||
for face in &mut group.faces {
|
||||
face.source_priority = self
|
||||
.web_font_order
|
||||
.get(&face.source_slot)
|
||||
.copied()
|
||||
.unwrap_or((
|
||||
!self.web_fonts[&face.source_slot]
|
||||
.membership
|
||||
.is_stylesheet_only(),
|
||||
usize::MAX,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// Physical families are private per slot. Changing CSS selection order
|
||||
// only updates candidate priorities, never Fontique registrations.
|
||||
self.refresh_web_font_priorities();
|
||||
let changed = current != previous;
|
||||
if changed && let Some(parley) = self.parley.as_mut() {
|
||||
parley.invalidate_font_selection();
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
fn refresh_web_font_priorities(&mut self) {
|
||||
let Some(parley) = self.parley.as_mut() else {
|
||||
return;
|
||||
};
|
||||
for family in parley.web_font_families.values_mut() {
|
||||
for group in &mut family.groups {
|
||||
for face in &mut group.faces {
|
||||
face.source_priority = self
|
||||
.web_font_order
|
||||
.get(&face.source_slot)
|
||||
.copied()
|
||||
.unwrap_or((
|
||||
!self.web_fonts[&face.source_slot]
|
||||
.membership
|
||||
.is_stylesheet_only(),
|
||||
usize::MAX,
|
||||
));
|
||||
}
|
||||
group.faces.sort_by(|first, second| {
|
||||
(first.source_priority, first.source_slot.as_str())
|
||||
.cmp(&(second.source_priority, second.source_slot.as_str()))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_font_collection(&mut self) {
|
||||
if self.parley.is_some() {
|
||||
self.parley = Some(Box::new(build_parley_services(
|
||||
self.system_font_policy,
|
||||
&self.web_fonts,
|
||||
&self.web_font_order,
|
||||
)));
|
||||
#[cfg(test)]
|
||||
{
|
||||
self.font_collection_rebuilds += 1;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Adds or replaces one owner-validated font face.
|
||||
@@ -1024,21 +1064,27 @@ impl DocumentLayoutServices {
|
||||
source_key: registration.source_key,
|
||||
membership,
|
||||
};
|
||||
let outcome = match self.web_fonts.get(®istration.slot) {
|
||||
Some(current)
|
||||
if current.face == font.face
|
||||
&& current.membership == font.membership
|
||||
&& current.source_key == font.source_key
|
||||
&& current.sfnt_bytes.as_ref() == font.sfnt_bytes.as_ref() =>
|
||||
{
|
||||
WebFontRegistrationOutcome::Unchanged
|
||||
if let Some(current) = self.web_fonts.get_mut(®istration.slot)
|
||||
&& current.face == font.face
|
||||
&& (current.sfnt_bytes.id() == font.sfnt_bytes.id()
|
||||
|| current.sfnt_bytes.as_ref() == font.sfnt_bytes.as_ref())
|
||||
{
|
||||
if current.membership == font.membership && current.source_key == font.source_key {
|
||||
return Ok(WebFontRegistrationOutcome::Unchanged);
|
||||
}
|
||||
Some(_) => WebFontRegistrationOutcome::Replaced,
|
||||
None => WebFontRegistrationOutcome::Added,
|
||||
};
|
||||
if outcome == WebFontRegistrationOutcome::Unchanged {
|
||||
return Ok(outcome);
|
||||
current.membership = font.membership;
|
||||
current.source_key = font.source_key;
|
||||
self.refresh_web_font_priorities();
|
||||
if let Some(parley) = self.parley.as_mut() {
|
||||
parley.invalidate_font_selection();
|
||||
}
|
||||
return Ok(WebFontRegistrationOutcome::Replaced);
|
||||
}
|
||||
let outcome = if self.web_fonts.contains_key(®istration.slot) {
|
||||
WebFontRegistrationOutcome::Replaced
|
||||
} else {
|
||||
WebFontRegistrationOutcome::Added
|
||||
};
|
||||
// Fontique's successful registration is the validation for an added
|
||||
// face. Keep it in the live collection instead of trial-registering in
|
||||
// a throwaway collection and then rebuilding every existing family.
|
||||
@@ -1057,16 +1103,9 @@ impl DocumentLayoutServices {
|
||||
validate_registered_font(&font.face, font.sfnt_bytes.clone())?;
|
||||
}
|
||||
self.web_fonts.insert(registration.slot, font);
|
||||
if self.parley.is_some() {
|
||||
// Replacement still needs a rebuild: Fontique can unregister by
|
||||
// family/attributes, not by our exact source slot. Removing that
|
||||
// match would also remove other unicode-range faces sharing it.
|
||||
self.parley = Some(Box::new(build_parley_services(
|
||||
self.system_font_policy,
|
||||
&self.web_fonts,
|
||||
&self.web_font_order,
|
||||
)));
|
||||
}
|
||||
// Replacing bytes/descriptors must rebuild: unregistering by Fontique
|
||||
// attributes could also remove another slot with the same attributes.
|
||||
self.rebuild_font_collection();
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
@@ -1075,52 +1114,69 @@ impl DocumentLayoutServices {
|
||||
if self.web_fonts.remove(slot).is_none() {
|
||||
return false;
|
||||
}
|
||||
if self.parley.is_some() {
|
||||
self.parley = Some(Box::new(build_parley_services(
|
||||
self.system_font_policy,
|
||||
&self.web_fonts,
|
||||
&self.web_font_order,
|
||||
)));
|
||||
}
|
||||
self.rebuild_font_collection();
|
||||
true
|
||||
}
|
||||
|
||||
pub fn remove_stylesheet_web_font(&mut self, slot: &str) -> bool {
|
||||
self.remove_web_font_membership(slot, WebFontRegistrationScope::Stylesheet)
|
||||
self.remove_stylesheet_web_fonts([slot])
|
||||
}
|
||||
|
||||
/// Retires a CSS manifest update in one collection transaction.
|
||||
pub fn remove_stylesheet_web_fonts<'a>(
|
||||
&mut self,
|
||||
slots: impl IntoIterator<Item = &'a str>,
|
||||
) -> bool {
|
||||
self.remove_web_font_memberships(slots, WebFontRegistrationScope::Stylesheet)
|
||||
}
|
||||
|
||||
pub fn remove_font_face_set_web_font(&mut self, slot: &str) -> bool {
|
||||
self.remove_web_font_membership(slot, WebFontRegistrationScope::FontFaceSetManual)
|
||||
self.remove_font_face_set_web_fonts([slot])
|
||||
}
|
||||
|
||||
fn remove_web_font_membership(&mut self, slot: &str, scope: WebFontRegistrationScope) -> bool {
|
||||
let Some(font) = self.web_fonts.get_mut(slot) else {
|
||||
return false;
|
||||
};
|
||||
match scope {
|
||||
WebFontRegistrationScope::Stylesheet => {
|
||||
if !std::mem::take(&mut font.membership.stylesheet) {
|
||||
return false;
|
||||
/// Retires a FontFaceSet membership update in one collection transaction.
|
||||
pub fn remove_font_face_set_web_fonts<'a>(
|
||||
&mut self,
|
||||
slots: impl IntoIterator<Item = &'a str>,
|
||||
) -> bool {
|
||||
self.remove_web_font_memberships(slots, WebFontRegistrationScope::FontFaceSetManual)
|
||||
}
|
||||
|
||||
fn remove_web_font_memberships<'a>(
|
||||
&mut self,
|
||||
slots: impl IntoIterator<Item = &'a str>,
|
||||
scope: WebFontRegistrationScope,
|
||||
) -> bool {
|
||||
let mut changed = false;
|
||||
let mut removed = false;
|
||||
for slot in slots {
|
||||
let Some(font) = self.web_fonts.get_mut(slot) else {
|
||||
continue;
|
||||
};
|
||||
let retired = match scope {
|
||||
WebFontRegistrationScope::Stylesheet => {
|
||||
std::mem::take(&mut font.membership.stylesheet)
|
||||
}
|
||||
}
|
||||
WebFontRegistrationScope::FontFaceSetConnected
|
||||
| WebFontRegistrationScope::FontFaceSetManual => {
|
||||
if font.membership.font_face_set.take().is_none() {
|
||||
return false;
|
||||
WebFontRegistrationScope::FontFaceSetConnected
|
||||
| WebFontRegistrationScope::FontFaceSetManual => {
|
||||
font.membership.font_face_set.take().is_some()
|
||||
}
|
||||
};
|
||||
changed |= retired;
|
||||
if retired && font.membership.is_empty() {
|
||||
self.web_fonts.remove(slot);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if font.membership.is_empty() {
|
||||
return self.remove_web_font(slot);
|
||||
if removed {
|
||||
self.rebuild_font_collection();
|
||||
} else if changed {
|
||||
self.refresh_web_font_priorities();
|
||||
if let Some(parley) = self.parley.as_mut() {
|
||||
parley.invalidate_font_selection();
|
||||
}
|
||||
}
|
||||
if self.parley.is_some() {
|
||||
self.parley = Some(Box::new(build_parley_services(
|
||||
self.system_font_policy,
|
||||
&self.web_fonts,
|
||||
&self.web_font_order,
|
||||
)));
|
||||
}
|
||||
true
|
||||
changed
|
||||
}
|
||||
|
||||
pub fn has_stylesheet_web_fonts(&self) -> bool {
|
||||
@@ -1200,8 +1256,20 @@ impl DocumentLayoutServices {
|
||||
(font.membership.is_stylesheet_only() && !is_active(slot)).then_some(slot.as_str())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if excluded.is_empty() && additions.is_empty() && order == self.web_font_order {
|
||||
return consume(self);
|
||||
if excluded.is_empty() && additions.is_empty() {
|
||||
if order == self.web_font_order {
|
||||
return consume(self);
|
||||
}
|
||||
let previous = std::mem::replace(&mut self.web_font_order, order);
|
||||
self.refresh_web_font_priorities();
|
||||
if let Some(parley) = self.parley.as_mut() {
|
||||
parley.invalidate_font_selection();
|
||||
}
|
||||
let scoped = ScopedWebFontOrder {
|
||||
services: self,
|
||||
previous,
|
||||
};
|
||||
return consume(scoped.services);
|
||||
}
|
||||
// BTreeMap iteration keeps the excluded slots sorted. Font bytes are
|
||||
// shared Arcs; a temporary selection never copies their payloads.
|
||||
@@ -1220,6 +1288,8 @@ impl DocumentLayoutServices {
|
||||
web_font_order: order,
|
||||
has_font_face_set_order: self.has_font_face_set_order,
|
||||
text_layout_passes: 0,
|
||||
#[cfg(test)]
|
||||
font_collection_rebuilds: 0,
|
||||
};
|
||||
let result = consume(&mut temporary);
|
||||
self.text_layout_passes = self
|
||||
|
||||
@@ -453,3 +453,164 @@ fn unchanged_bytes_do_not_depend_on_blob_allocation_identity() {
|
||||
assert_eq!(source_id(services.parley_mut(), "Same"), source);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reordered_overlapping_fonts_keep_sources_contexts_and_invalidate_selection() {
|
||||
let mut services = DocumentLayoutServices::with_system_font_policy(SystemFontPolicy::Disabled);
|
||||
let narrow = include_bytes!("../../../tests/fixtures/moli-ahem-narrow.ttf");
|
||||
let wide = include_bytes!("../../../tests/fixtures/moli-ahem-wide.ttf");
|
||||
for (slot, bytes, weight) in [
|
||||
("wide", wide.as_slice(), "100 900"),
|
||||
("narrow", narrow.as_slice(), "500 800"),
|
||||
] {
|
||||
services
|
||||
.register_web_font(WebFontRegistration::new(
|
||||
slot,
|
||||
WebFontFace::new("Ordered").with_css_descriptors(weight, "normal", "normal"),
|
||||
bytes.to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
services
|
||||
.register_web_font(registration("seed", "Seed", TEST_TTF))
|
||||
.unwrap();
|
||||
let original_source = source_id(services.parley_mut(), "Seed");
|
||||
let context = std::ptr::from_ref(&services.parley_mut().font_context);
|
||||
let layout = std::ptr::from_ref(&services.parley_mut().layout_context);
|
||||
for (order, bytes) in [
|
||||
(["wide", "narrow"], narrow.as_slice()),
|
||||
(["narrow", "wide"], wide.as_slice()),
|
||||
(["wide", "narrow"], narrow.as_slice()),
|
||||
] {
|
||||
services.set_web_font_order(&[], &order.map(str::to_owned));
|
||||
assert_eq!(
|
||||
shape_one_character(services.parley_mut(), web_font_style("Ordered", 700.0), 'A'),
|
||||
bytes
|
||||
);
|
||||
assert_eq!(source_id(services.parley_mut(), "Seed"), original_source);
|
||||
assert_eq!(
|
||||
std::ptr::from_ref(&services.parley_mut().font_context),
|
||||
context
|
||||
);
|
||||
assert_eq!(
|
||||
std::ptr::from_ref(&services.parley_mut().layout_context),
|
||||
layout
|
||||
);
|
||||
assert_eq!(services.font_collection_rebuilds, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_membership_retirement_rebuilds_once_and_preserves_other_owners() {
|
||||
for scope in [
|
||||
WebFontRegistrationScope::Stylesheet,
|
||||
WebFontRegistrationScope::FontFaceSetManual,
|
||||
] {
|
||||
let mut services =
|
||||
DocumentLayoutServices::with_system_font_policy(SystemFontPolicy::Disabled);
|
||||
for index in 0..64 {
|
||||
services
|
||||
.register_web_font(
|
||||
registration(&format!("slot-{index}"), "Shared", TEST_TTF).with_scope(scope),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
services
|
||||
.register_web_font(registration("retained", "Shared", TEST_TTF).with_stylesheet_scope())
|
||||
.unwrap();
|
||||
services
|
||||
.register_web_font(registration("retained", "Shared", TEST_TTF))
|
||||
.unwrap();
|
||||
services.parley_mut();
|
||||
let mut slots = (0..64)
|
||||
.map(|index| format!("slot-{index}"))
|
||||
.collect::<Vec<_>>();
|
||||
slots.extend(["retained".into(), "missing".into(), "slot-0".into()]);
|
||||
let retire = |services: &mut DocumentLayoutServices| match scope {
|
||||
WebFontRegistrationScope::Stylesheet => {
|
||||
services.remove_stylesheet_web_fonts(slots.iter().map(String::as_str))
|
||||
}
|
||||
_ => services.remove_font_face_set_web_fonts(slots.iter().map(String::as_str)),
|
||||
};
|
||||
assert!(retire(&mut services));
|
||||
assert_eq!(services.font_collection_rebuilds, 1);
|
||||
assert_eq!(services.web_font_count(), 1);
|
||||
assert_eq!(
|
||||
shape_one_character(services.parley_mut(), web_font_style("Shared", 625.0), 'R'),
|
||||
TEST_TTF
|
||||
);
|
||||
assert!(!retire(&mut services));
|
||||
assert_eq!(services.font_collection_rebuilds, 1);
|
||||
let source = source_id(services.parley_mut(), "Shared");
|
||||
// Adding and retiring an independent relation must retain the source.
|
||||
services
|
||||
.register_web_font(registration("retained", "Shared", TEST_TTF).with_scope(scope))
|
||||
.unwrap();
|
||||
assert!(retire(&mut services));
|
||||
assert_eq!(source_id(services.parley_mut(), "Shared"), source);
|
||||
assert_eq!(services.font_collection_rebuilds, 1);
|
||||
assert!(services.remove_web_font("retained"));
|
||||
assert_eq!(services.font_collection_rebuilds, 2);
|
||||
assert_eq!(services.web_font_count(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_css_order_keeps_sources_and_restores_selection_after_an_error() {
|
||||
let mut services = DocumentLayoutServices::with_system_font_policy(SystemFontPolicy::Disabled);
|
||||
let narrow = include_bytes!("../../../tests/fixtures/moli-ahem-narrow.ttf");
|
||||
let wide = include_bytes!("../../../tests/fixtures/moli-ahem-wide.ttf");
|
||||
let face = WebFontFace::new("ScopedOrder").with_css_descriptors("100 900", "normal", "normal");
|
||||
for (slot, bytes) in [("wide", wide.as_slice()), ("narrow", narrow.as_slice())] {
|
||||
services
|
||||
.register_web_font(
|
||||
WebFontRegistration::new(slot, face.clone(), bytes.to_vec())
|
||||
.with_stylesheet_scope(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
services
|
||||
.register_web_font(registration("seed", "Seed", TEST_TTF))
|
||||
.unwrap();
|
||||
services.set_stylesheet_web_font_order(&["wide".into(), "narrow".into()]);
|
||||
let source = source_id(services.parley_mut(), "Seed");
|
||||
let context = std::ptr::from_ref(&services.parley_mut().font_context);
|
||||
let result: Result<(), ()> = services.with_stylesheet_font_declarations(
|
||||
["narrow", "wide"].map(|slot| WebFontDeclaration {
|
||||
slot,
|
||||
source_key: "fixture",
|
||||
face: &face,
|
||||
}),
|
||||
|temporary| {
|
||||
assert_eq!(
|
||||
shape_one_character(
|
||||
temporary.parley_mut(),
|
||||
web_font_style("ScopedOrder", 700.0),
|
||||
'A'
|
||||
),
|
||||
wide
|
||||
);
|
||||
assert_eq!(source_id(temporary.parley_mut(), "Seed"), source);
|
||||
assert_eq!(
|
||||
std::ptr::from_ref(&temporary.parley_mut().font_context),
|
||||
context
|
||||
);
|
||||
Err(())
|
||||
},
|
||||
);
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
shape_one_character(
|
||||
services.parley_mut(),
|
||||
web_font_style("ScopedOrder", 700.0),
|
||||
'A'
|
||||
),
|
||||
narrow
|
||||
);
|
||||
assert_eq!(source_id(services.parley_mut(), "Seed"), source);
|
||||
assert_eq!(
|
||||
std::ptr::from_ref(&services.parley_mut().font_context),
|
||||
context
|
||||
);
|
||||
assert_eq!(services.font_collection_rebuilds, 0);
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ pub(crate) fn finish_font_face_url_load<'s>(
|
||||
|
||||
pub(super) fn sync_owners<'s>(scope: &mut v8::PinScope<'s, '_>, face: v8::Local<'s, v8::Object>) {
|
||||
for owner in super::storage::font_face_set_owner_snapshot(scope, face) {
|
||||
sync_registration(scope, owner, face, true);
|
||||
sync_registration(scope, owner, face);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +360,6 @@ pub(super) fn sync_registration<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
owner: v8::Local<'s, v8::Object>,
|
||||
face: v8::Local<'s, v8::Object>,
|
||||
present: bool,
|
||||
) {
|
||||
let Some(document) = get_private_value(scope, owner, OWNER_DOCUMENT)
|
||||
.and_then(|value| crate::native_bridge::callback_value_dom_handle(scope, value))
|
||||
@@ -383,20 +382,16 @@ pub(super) fn sync_registration<'s>(
|
||||
let host = unsafe { &*host_ptr };
|
||||
let changed = host.with_font_services(document, |services| {
|
||||
let load = load.borrow();
|
||||
if present {
|
||||
match load.register(services, registration_scope) {
|
||||
Ok(Some(
|
||||
moli_layout::WebFontRegistrationOutcome::Added
|
||||
| moli_layout::WebFontRegistrationOutcome::Replaced,
|
||||
)) => true,
|
||||
Ok(_) => false,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "failed to register FontFace");
|
||||
false
|
||||
}
|
||||
match load.register(services, registration_scope) {
|
||||
Ok(Some(
|
||||
moli_layout::WebFontRegistrationOutcome::Added
|
||||
| moli_layout::WebFontRegistrationOutcome::Replaced,
|
||||
)) => true,
|
||||
Ok(_) => false,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "failed to register FontFace");
|
||||
false
|
||||
}
|
||||
} else {
|
||||
services.remove_font_face_set_web_font(load.slot())
|
||||
}
|
||||
});
|
||||
if changed {
|
||||
|
||||
@@ -346,21 +346,36 @@ fn sync_font_face_set_owners<'s>(
|
||||
retained_slots.insert(load.borrow().slot().to_owned());
|
||||
}
|
||||
}
|
||||
let mut removed_faces = Vec::new();
|
||||
let mut removed_slots = Vec::new();
|
||||
for index in 0..previous.length() {
|
||||
let Some(face) = previous.get_index(scope, index) else {
|
||||
continue;
|
||||
};
|
||||
if !array_contains_value(scope, current, face) {
|
||||
if let Ok(face) = v8::Local::<v8::Object>::try_from(face) {
|
||||
let retained = super::state::get(scope, face)
|
||||
.is_some_and(|load| retained_slots.contains(load.borrow().slot()));
|
||||
if !retained {
|
||||
super::loading::sync_registration(scope, owner, face, false);
|
||||
}
|
||||
if let Ok(face) = v8::Local::<v8::Object>::try_from(face)
|
||||
&& let Some(load) = super::state::get(scope, face)
|
||||
&& !retained_slots.contains(load.borrow().slot())
|
||||
{
|
||||
removed_slots.push(load.borrow().slot().to_owned());
|
||||
}
|
||||
remove_font_face_set_owner(scope, face, owner);
|
||||
removed_faces.push(face);
|
||||
}
|
||||
}
|
||||
if !removed_slots.is_empty()
|
||||
&& let Some((host_ptr, document)) = font_face_set_document_context(scope, owner)
|
||||
{
|
||||
// SAFETY: native font membership updates cannot call into page code.
|
||||
let host = unsafe { &*host_ptr };
|
||||
if host.with_font_services(document, |services| {
|
||||
services.remove_font_face_set_web_fonts(removed_slots.iter().map(String::as_str))
|
||||
}) {
|
||||
host.invalidate_document_font_layout(document);
|
||||
}
|
||||
}
|
||||
for face in removed_faces {
|
||||
remove_font_face_set_owner(scope, face, owner);
|
||||
}
|
||||
for index in 0..current.length() {
|
||||
let Some(face) = current.get_index(scope, index) else {
|
||||
continue;
|
||||
@@ -368,7 +383,7 @@ fn sync_font_face_set_owners<'s>(
|
||||
if !array_contains_value(scope, previous, face) {
|
||||
add_font_face_set_owner(scope, face, owner);
|
||||
if let Ok(face) = v8::Local::<v8::Object>::try_from(face) {
|
||||
super::loading::sync_registration(scope, owner, face, true);
|
||||
super::loading::sync_registration(scope, owner, face);
|
||||
if super::loading::status(scope, face)
|
||||
== crate::font_loading::FontFaceStatus::Loading
|
||||
{
|
||||
|
||||
@@ -135,14 +135,14 @@ impl DocumentWebFontState {
|
||||
.map(|font| font.registration_slot().to_owned())
|
||||
.collect::<Vec<_>>();
|
||||
let active = order.iter().collect::<BTreeSet<_>>();
|
||||
let mut changed = false;
|
||||
self.slots.retain(|slot, _| {
|
||||
if active.contains(slot) {
|
||||
return true;
|
||||
}
|
||||
changed |= services.remove_stylesheet_web_font(slot);
|
||||
false
|
||||
});
|
||||
let removed = self
|
||||
.slots
|
||||
.keys()
|
||||
.filter(|slot| !active.contains(slot))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
self.slots.retain(|slot, _| active.contains(slot));
|
||||
let changed = services.remove_stylesheet_web_fonts(removed.iter().map(String::as_str));
|
||||
self.loads.retain(|_, load| load.strong_count() != 0);
|
||||
self.sources.retain(|_, source| source.strong_count() != 0);
|
||||
changed | services.set_stylesheet_web_font_order(&order)
|
||||
|
||||
Reference in New Issue
Block a user