mirror of
https://github.com/lexmount/moli.git
synced 2026-10-30 16:07:24 +00:00
fix(fonts): publish CSS declaration order without JS projection
This commit is contained in:
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from ..assertions import assert_equal, record_contract
|
||||
from ..config import REPO_ROOT
|
||||
from .font_face_css_order import run_css_font_order_contract, run_projected_local_font_order_contract
|
||||
from .font_face_matching import run_font_matching_contract
|
||||
|
||||
|
||||
@@ -13,6 +14,8 @@ async def run_font_face_payload_group(
|
||||
fixture: str,
|
||||
results: list[dict[str, Any]],
|
||||
) -> None:
|
||||
await run_css_font_order_contract(browser, results)
|
||||
await run_projected_local_font_order_contract(browser, results)
|
||||
context = await browser.new_context()
|
||||
try:
|
||||
page = await context.new_page()
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from typing import Any
|
||||
|
||||
from ..assertions import assert_equal, record_contract
|
||||
from .font_face_matching import font_matching_payload
|
||||
|
||||
|
||||
async def run_css_font_order_contract(browser: Any, results: list[dict[str, Any]]) -> None:
|
||||
context = await browser.new_context()
|
||||
try:
|
||||
fonts = font_matching_payload()
|
||||
observed = {}
|
||||
for names in [("narrow", "wide"), ("wide", "narrow")]:
|
||||
rules = "".join(
|
||||
"@font-face{font-family:PureCssOrder;src:url(data:font/ttf;base64,"
|
||||
+ fonts[name + "Data"] + ")}"
|
||||
for name in names
|
||||
)
|
||||
html = (
|
||||
"<!doctype html><script>globalThis.fontsReads=0;"
|
||||
"Object.defineProperty(document,'fonts',{get(){++fontsReads;"
|
||||
"throw new Error('document.fonts must remain unobserved')}})</script>"
|
||||
"<style>" + rules + "</style>"
|
||||
'<span id="probe" style="display:inline-block;font:40px PureCssOrder">AAAA</span>'
|
||||
)
|
||||
page = await context.new_page()
|
||||
try:
|
||||
url = "data:text/html;base64," + base64.b64encode(html.encode()).decode("ascii")
|
||||
await page.goto(url, wait_until="load", timeout=10_000)
|
||||
observed["-".join(names)] = await page.evaluate(
|
||||
"({width: document.getElementById('probe').getBoundingClientRect().width, fontsReads})"
|
||||
)
|
||||
finally:
|
||||
await page.close()
|
||||
assert_equal(observed, {
|
||||
"narrow-wide": {"width": 160, "fontsReads": 0},
|
||||
"wide-narrow": {"width": 80, "fontsReads": 0},
|
||||
}, "CSS font declaration order without observing document.fonts")
|
||||
record_contract(
|
||||
results,
|
||||
"css_font_order_without_font_face_set_projection",
|
||||
contract="Pure CSS font selection follows reverse declaration order without accessing document.fonts.",
|
||||
source="Chromium 147 and pinned Chromium 145.0.7632.6",
|
||||
commands=["Page.navigate", "Runtime.evaluate"],
|
||||
observed=observed,
|
||||
)
|
||||
finally:
|
||||
await context.close()
|
||||
|
||||
|
||||
async def run_projected_local_font_order_contract(browser: Any, results: list[dict[str, Any]]) -> None:
|
||||
context = await browser.new_context()
|
||||
try:
|
||||
page = await context.new_page()
|
||||
await page.goto("about:blank", wait_until="load", timeout=10_000)
|
||||
observed = await page.evaluate(r"""async ({narrowData, wideData}) => {
|
||||
let localName;
|
||||
for (const name of ['DejaVu Sans Mono', 'Liberation Mono', 'Courier New', 'Menlo', 'Consolas']) {
|
||||
const face = new FontFace('AvailableLocalOrderFont', `local(${JSON.stringify(name)})`);
|
||||
try { await face.load(); localName = name; break; } catch {}
|
||||
}
|
||||
if (!localName) throw new Error('No supported local monospace font is installed');
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `@font-face{font-family:ProjectedLocalOrder;src:local(${JSON.stringify(localName)})}
|
||||
@font-face{font-family:ProjectedLocalOrder;src:url(data:font/ttf;base64,${wideData})}`;
|
||||
document.head.append(style);
|
||||
const faces = [...document.fonts].filter(face => face.family === 'ProjectedLocalOrder');
|
||||
await Promise.all(faces.map(face => face.load()));
|
||||
globalThis.localOrderProbe = document.createElement('span');
|
||||
localOrderProbe.textContent = 'AAAA';
|
||||
localOrderProbe.style.cssText = 'display:inline-block;font:40px ProjectedLocalOrder';
|
||||
document.body.append(localOrderProbe);
|
||||
const before = localOrderProbe.getBoundingClientRect().width;
|
||||
const other = document.createElement('style');
|
||||
other.textContent = `@font-face{font-family:UnrelatedLocalOrder;src:url(data:font/ttf;base64,${narrowData})}`;
|
||||
document.head.append(other);
|
||||
return {before, states: faces.map(face => face.status)};
|
||||
}""", font_matching_payload())
|
||||
# Observe after the stylesheet mutation's native resource reconciliation,
|
||||
# without rereading document.fonts to republish the projected order.
|
||||
observed["after"] = await page.evaluate("localOrderProbe.getBoundingClientRect().width")
|
||||
assert_equal(observed, {"before": 160, "states": ["loaded", "loaded"], "after": 160},
|
||||
"native font fetch updates preserve projected local-only CSS face order")
|
||||
record_contract(
|
||||
results,
|
||||
"projected_local_css_font_order_survives_native_reconciliation",
|
||||
contract="An exposed FontFaceSet retains complete CSS order, including local-only faces, when native font requests change.",
|
||||
source="Chromium 147 and pinned Chromium 145.0.7632.6",
|
||||
commands=["Runtime.evaluate"],
|
||||
observed=observed,
|
||||
)
|
||||
finally:
|
||||
await context.close()
|
||||
@@ -709,6 +709,7 @@ pub struct DocumentLayoutServices {
|
||||
// Resource slots retain identity across removals and shared FontFaces.
|
||||
// Selection priority belongs to this Document's current FontFaceSet.
|
||||
web_font_order: BTreeMap<String, (bool, usize)>,
|
||||
has_font_face_set_order: bool,
|
||||
pub(crate) text_layout_passes: u64,
|
||||
}
|
||||
|
||||
@@ -727,6 +728,7 @@ impl DocumentLayoutServices {
|
||||
system_font_policy: SystemFontPolicy::Enabled,
|
||||
web_fonts: BTreeMap::new(),
|
||||
web_font_order: BTreeMap::new(),
|
||||
has_font_face_set_order: false,
|
||||
text_layout_passes: 0,
|
||||
}
|
||||
}
|
||||
@@ -739,6 +741,7 @@ impl DocumentLayoutServices {
|
||||
system_font_policy,
|
||||
web_fonts: BTreeMap::new(),
|
||||
web_font_order: BTreeMap::new(),
|
||||
has_font_face_set_order: false,
|
||||
text_layout_passes: 0,
|
||||
}
|
||||
}
|
||||
@@ -765,6 +768,7 @@ impl DocumentLayoutServices {
|
||||
/// this owner. Unloaded faces keep their place for a later completion.
|
||||
/// Returns true only when registered candidates actually change priority.
|
||||
pub fn set_web_font_order(&mut self, stylesheet: &[String], manual: &[String]) -> bool {
|
||||
self.has_font_face_set_order = true;
|
||||
let order = stylesheet
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -776,6 +780,25 @@ impl DocumentLayoutServices {
|
||||
.map(|(index, slot)| (slot.clone(), (true, index))),
|
||||
)
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
self.update_web_font_order(order)
|
||||
}
|
||||
|
||||
/// Publishes native stylesheet order until a JS FontFaceSet is exposed.
|
||||
/// Its complete order also includes local-only CSS faces, which have no
|
||||
/// fetch-manifest entry. Later native fetch updates must preserve it.
|
||||
pub fn set_stylesheet_web_font_order(&mut self, stylesheet: &[String]) -> bool {
|
||||
if self.has_font_face_set_order {
|
||||
return false;
|
||||
}
|
||||
let order = stylesheet
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, slot)| (slot.clone(), (false, index)))
|
||||
.collect();
|
||||
self.update_web_font_order(order)
|
||||
}
|
||||
|
||||
fn update_web_font_order(&mut self, order: BTreeMap<String, (bool, usize)>) -> bool {
|
||||
if self.web_font_order == order {
|
||||
return false;
|
||||
}
|
||||
@@ -895,6 +918,7 @@ impl DocumentLayoutServices {
|
||||
system_font_policy: self.system_font_policy,
|
||||
web_fonts,
|
||||
web_font_order: self.web_font_order.clone(),
|
||||
has_font_face_set_order: self.has_font_face_set_order,
|
||||
text_layout_passes: 0,
|
||||
};
|
||||
let result = consume(&mut temporary);
|
||||
@@ -1377,6 +1401,48 @@ mod tests {
|
||||
.to_vec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stylesheet_fetch_order_preserves_projected_local_font_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");
|
||||
// Local-only CSS wrappers have a script resource slot rather than a
|
||||
// stylesheet fetch entry. The complete FontFaceSet gives their order.
|
||||
services
|
||||
.register_web_font(WebFontRegistration::new(
|
||||
"local-css",
|
||||
WebFontFace::new("CSS Local Order"),
|
||||
narrow.to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
services
|
||||
.register_web_font(
|
||||
WebFontRegistration::new(
|
||||
"url-css",
|
||||
WebFontFace::new("CSS Local Order"),
|
||||
wide.to_vec(),
|
||||
)
|
||||
.with_stylesheet_scope(),
|
||||
)
|
||||
.unwrap();
|
||||
services.set_web_font_order(&["local-css".into(), "url-css".into()], &[]);
|
||||
let selected = |services: &mut DocumentLayoutServices| {
|
||||
shape_one_character(
|
||||
services.parley_mut(),
|
||||
web_font_style("CSS Local Order", 400.0),
|
||||
'A',
|
||||
)
|
||||
};
|
||||
assert_eq!(selected(&mut services), wide);
|
||||
services.set_stylesheet_web_font_order(&["url-css".into(), "new-css".into()]);
|
||||
assert_eq!(selected(&mut services), wide);
|
||||
services.set_web_font_order(&["url-css".into(), "local-css".into()], &[]);
|
||||
assert_eq!(selected(&mut services), narrow);
|
||||
services.set_stylesheet_web_font_order(&["url-css".into()]);
|
||||
assert_eq!(selected(&mut services), narrow);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stylesheet_font_selection_preserves_native_faces_and_restores_after_error() {
|
||||
let mut services =
|
||||
|
||||
@@ -645,11 +645,14 @@ impl JsContextHost {
|
||||
document: DomHandle,
|
||||
resources: impl IntoIterator<Item = &'a StylesheetLoadBlockingResource>,
|
||||
) {
|
||||
self.document_layout_state.borrow_mut().retain_active_slots(
|
||||
let changed = self.document_layout_state.borrow_mut().retain_active_slots(
|
||||
document,
|
||||
self.document_handle(),
|
||||
resources,
|
||||
);
|
||||
if changed {
|
||||
self.invalidate_document_font_layout(document);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn admit_document_web_font(
|
||||
|
||||
@@ -308,20 +308,27 @@ impl DocumentLayoutState {
|
||||
document: DomHandle,
|
||||
main_document: DomHandle,
|
||||
resources: impl IntoIterator<Item = &'a StylesheetLoadBlockingResource>,
|
||||
) {
|
||||
) -> bool {
|
||||
if document == main_document {
|
||||
return self
|
||||
.web_fonts
|
||||
.retain_active_slots(resources, &mut self.services);
|
||||
}
|
||||
let mut resources = resources.into_iter().peekable();
|
||||
if resources.peek().is_some() {
|
||||
return self.with_web_fonts_for_document(document, main_document, |fonts, services| {
|
||||
fonts.retain_active_slots(resources, services)
|
||||
});
|
||||
}
|
||||
if let Some(fonts) = self
|
||||
.embedded_web_fonts
|
||||
.as_mut()
|
||||
.and_then(|fonts| fonts.get_mut(&document))
|
||||
&& let Some(services) = self.embedded_document_services.get_mut(&document)
|
||||
{
|
||||
fonts.retain_active_slots(resources, services);
|
||||
return fonts.retain_active_slots(resources, services);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn admit(
|
||||
|
||||
@@ -23,6 +23,131 @@ fn order_vm() -> StandaloneScriptVmHarness {
|
||||
vm
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn font_face_css_selection_keeps_declaration_order_without_js_projection() {
|
||||
use crate::native_bridge::document::take_owner_font_face_projection_count_for_test;
|
||||
|
||||
let (narrow, wide) = order_font_data();
|
||||
for (sources, expected) in [([&narrow, &wide], "160"), ([&wide, &narrow], "80")] {
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
loader.set_optional_resource_fetch_mask(
|
||||
crate::protocol_types::OptionalResourceFetchMask::FONT,
|
||||
);
|
||||
let (mut vm, _completions) = new_parsed_test_vm_with_loader_and_resource_completion_queue(
|
||||
"https://font-selection-order.test/",
|
||||
"<!doctype html><html><head></head><body><span id='probe' style='display:inline-block;font:40px OrderFace'>AAAA</span></body></html>",
|
||||
&loader,
|
||||
);
|
||||
vm.set_layout_policy(moli_page_types::LayoutPolicy::OnDemand);
|
||||
take_owner_font_face_projection_count_for_test();
|
||||
vm.eval(&format!(r#"
|
||||
Object.defineProperty(document, 'fonts', {{get() {{throw new Error('unexpected document.fonts access')}}}});
|
||||
const style=document.createElement('style');
|
||||
style.textContent='@font-face{{font-family:OrderFace;src:url(data:font/ttf;base64,{})}}@font-face{{font-family:OrderFace;src:url(data:font/ttf;base64,{})}}';
|
||||
document.head.append(style);
|
||||
"#, sources[0], sources[1])).unwrap();
|
||||
vm.reconcile_document_web_fonts_for_layout();
|
||||
assert_eq!(vm.document_web_font_counts_for_test(), (2, 2, 2));
|
||||
assert_eq!(
|
||||
vm.eval("String(probe.getBoundingClientRect().width)")
|
||||
.unwrap(),
|
||||
expected
|
||||
);
|
||||
assert_eq!(take_owner_font_face_projection_count_for_test(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn font_face_css_reordering_preserves_last_duplicate_and_manual_priority() {
|
||||
use crate::native_bridge::document::take_owner_font_face_projection_count_for_test;
|
||||
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
loader.set_optional_resource_fetch_mask(crate::protocol_types::OptionalResourceFetchMask::FONT);
|
||||
let (mut vm, _completions) = new_parsed_test_vm_with_loader_and_resource_completion_queue(
|
||||
"https://font-selection-order.test/",
|
||||
"<!doctype html><html><head></head><body><span id='probe' style='display:inline-block;font:40px OrderFace'>AAAA</span></body></html>",
|
||||
&loader,
|
||||
);
|
||||
vm.set_layout_policy(moli_page_types::LayoutPolicy::OnDemand);
|
||||
let (narrow, wide) = order_font_data();
|
||||
take_owner_font_face_projection_count_for_test();
|
||||
vm.eval(&format!(r#"
|
||||
globalThis.styles=["{narrow}","{wide}"].map(data=>{{
|
||||
const style=document.createElement('style');
|
||||
style.textContent=`@font-face{{font-family:OrderFace;src:url(data:font/ttf;base64,${{data}})}}`;
|
||||
document.head.append(style);return style;
|
||||
}});
|
||||
"#)).unwrap();
|
||||
let width = |vm: &mut StandaloneScriptVmHarness| {
|
||||
vm.reconcile_document_web_fonts_for_layout();
|
||||
assert_eq!(vm.document_web_font_counts_for_test(), (2, 2, 2));
|
||||
vm.eval("String(probe.getBoundingClientRect().width)")
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(width(&mut vm), "160");
|
||||
vm.eval("document.head.append(styles[0])").unwrap();
|
||||
assert_eq!(width(&mut vm), "80");
|
||||
vm.eval("globalThis.duplicate=styles[1].cloneNode(true);document.head.append(duplicate)")
|
||||
.unwrap();
|
||||
assert_eq!(width(&mut vm), "160");
|
||||
vm.eval("duplicate.remove()").unwrap();
|
||||
assert_eq!(width(&mut vm), "80");
|
||||
assert_eq!(take_owner_font_face_projection_count_for_test(), 0);
|
||||
|
||||
vm.eval(&format!(r#"
|
||||
document.fonts.add(new FontFace('OrderFace',Uint8Array.from(atob('{wide}'),c=>c.charCodeAt(0))));
|
||||
document.head.append(styles[1]);
|
||||
"#)).unwrap();
|
||||
vm.reconcile_document_web_fonts_for_layout();
|
||||
assert_eq!(vm.document_web_font_counts_for_test(), (2, 2, 3));
|
||||
vm.eval("document.head.append(styles[0])").unwrap();
|
||||
vm.reconcile_document_web_fonts_for_layout();
|
||||
assert_eq!(
|
||||
vm.eval("String(probe.getBoundingClientRect().width)")
|
||||
.unwrap(),
|
||||
"160"
|
||||
);
|
||||
vm.eval("document.fonts.clear()").unwrap();
|
||||
assert_eq!(
|
||||
vm.eval("String(probe.getBoundingClientRect().width)")
|
||||
.unwrap(),
|
||||
"80"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn font_face_css_order_is_published_before_child_font_admission() {
|
||||
use crate::native_bridge::document::take_owner_font_face_projection_count_for_test;
|
||||
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
loader.set_optional_resource_fetch_mask(crate::protocol_types::OptionalResourceFetchMask::FONT);
|
||||
let (mut vm, _completions) = new_parsed_test_vm_with_loader_and_resource_completion_queue(
|
||||
"https://font-selection-order.test/",
|
||||
"<!doctype html><iframe id='frame'></iframe>",
|
||||
&loader,
|
||||
);
|
||||
vm.set_layout_policy(moli_page_types::LayoutPolicy::OnDemand);
|
||||
let (narrow, wide) = order_font_data();
|
||||
take_owner_font_face_projection_count_for_test();
|
||||
for (first, second, expected) in [(&narrow, &wide, "160"), (&wide, &narrow, "80")] {
|
||||
vm.eval(&format!(r#"
|
||||
globalThis.child=frame.contentDocument;
|
||||
child.open();
|
||||
child.write('<!doctype html><style>@font-face{{font-family:OrderFace;src:url(data:font/ttf;base64,{first})}}@font-face{{font-family:OrderFace;src:url(data:font/ttf;base64,{second})}}</style><span id="target" style="display:inline-block;font:40px OrderFace">AAAA</span>');
|
||||
child.close();
|
||||
Object.defineProperty(child,'fonts',{{configurable:true,get(){{throw new Error('unexpected child.fonts access')}}}});
|
||||
"#)).unwrap();
|
||||
vm.reconcile_document_web_fonts_for_layout();
|
||||
assert_eq!(
|
||||
vm.eval("String(child.getElementById('target').getBoundingClientRect().width)")
|
||||
.unwrap(),
|
||||
expected
|
||||
);
|
||||
assert_eq!(vm.document_web_font_counts_for_test(), (0, 0, 0));
|
||||
assert_eq!(take_owner_font_face_projection_count_for_test(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn font_face_selection_follows_owner_membership_instead_of_resource_id() {
|
||||
let mut vm = order_vm();
|
||||
|
||||
@@ -87,20 +87,23 @@ impl DocumentWebFontState {
|
||||
&mut self,
|
||||
resources: impl IntoIterator<Item = &'a StylesheetLoadBlockingResource>,
|
||||
services: &mut DocumentLayoutServices,
|
||||
) {
|
||||
let active = resources
|
||||
) -> bool {
|
||||
let order = resources
|
||||
.into_iter()
|
||||
.filter_map(StylesheetLoadBlockingResource::web_font)
|
||||
.map(|font| font.slot().to_owned())
|
||||
.collect::<BTreeSet<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
let active = order.iter().collect::<BTreeSet<_>>();
|
||||
let mut changed = false;
|
||||
self.slots.retain(|slot, entry| {
|
||||
if active.contains(slot) {
|
||||
return true;
|
||||
}
|
||||
entry.load.borrow().unregister(services);
|
||||
changed |= entry.load.borrow().unregister(services);
|
||||
false
|
||||
});
|
||||
self.loads.retain(|_, load| load.strong_count() != 0);
|
||||
changed | services.set_stylesheet_web_font_order(&order)
|
||||
}
|
||||
|
||||
fn bind_request(
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{css_resource_urls::StylesheetLoadBlockingResource, document_runtime::DomHandle};
|
||||
|
||||
@@ -94,7 +97,7 @@ impl StylesheetResourceManifest {
|
||||
) -> Self {
|
||||
#[cfg(test)]
|
||||
STYLESHEET_RESOURCE_MANIFEST_BUILD_COUNT.with(|count| count.set(count.get() + 1));
|
||||
let mut web_fonts = BTreeMap::new();
|
||||
let mut web_fonts = Vec::new();
|
||||
let mut imports = BTreeMap::new();
|
||||
collect_resources(
|
||||
document_stylesheets,
|
||||
@@ -115,8 +118,22 @@ impl StylesheetResourceManifest {
|
||||
&mut imports,
|
||||
);
|
||||
}
|
||||
// Slots share resource identity, but priority follows declarations.
|
||||
// Keep the last occurrence when identical rules reuse the same slot.
|
||||
let mut seen = BTreeSet::new();
|
||||
web_fonts.reverse();
|
||||
web_fonts.retain(|resource| {
|
||||
seen.insert(
|
||||
resource
|
||||
.web_font()
|
||||
.expect("font resource")
|
||||
.slot()
|
||||
.to_owned(),
|
||||
)
|
||||
});
|
||||
web_fonts.reverse();
|
||||
Self {
|
||||
web_fonts: web_fonts.into_values().collect::<Vec<_>>().into(),
|
||||
web_fonts: web_fonts.into(),
|
||||
imports: imports.into_values().collect::<Vec<_>>().into(),
|
||||
}
|
||||
}
|
||||
@@ -137,7 +154,7 @@ fn collect_resources(
|
||||
stylesheets: &ActiveStylesheetCollection,
|
||||
device: &style::device::Device,
|
||||
custom_media: &style::stylesheets::CustomMediaMap,
|
||||
web_fonts: &mut BTreeMap<String, StylesheetLoadBlockingResource>,
|
||||
web_fonts: &mut Vec<StylesheetLoadBlockingResource>,
|
||||
imports: &mut BTreeMap<String, url::Url>,
|
||||
) {
|
||||
for stylesheet in stylesheets.entries() {
|
||||
@@ -151,12 +168,9 @@ fn collect_resources(
|
||||
continue;
|
||||
}
|
||||
let resource = resource.resource();
|
||||
let Some(font) = resource.web_font() else {
|
||||
continue;
|
||||
};
|
||||
web_fonts
|
||||
.entry(font.slot().to_owned())
|
||||
.or_insert_with(|| resource.clone());
|
||||
if resource.web_font().is_some() {
|
||||
web_fonts.push(resource.clone());
|
||||
}
|
||||
}
|
||||
for import in stylesheet.import_urls() {
|
||||
let mut identity = import.clone();
|
||||
|
||||
Reference in New Issue
Block a user