mirror of
https://github.com/servo/servo.git
synced 2025-08-08 06:55:31 +01:00
Auto merge of #18486 - emilio:extra-data-to-cascade-data, r=SimonSapin
style: Move ExtraStyleData into CascadeData. It logically belongs there, and the only reason it wasn't there before we were working around other stuff. Now it's needed to share UA stylesheets across documents. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18486) <!-- Reviewable:end -->
This commit is contained in:
commit
1ab705b026
4 changed files with 69 additions and 42 deletions
|
@ -1198,11 +1198,9 @@ impl LayoutThread {
|
||||||
self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
|
self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut extra_data = Default::default();
|
|
||||||
self.stylist.flush(
|
self.stylist.flush(
|
||||||
&guards,
|
&guards,
|
||||||
Some(ua_stylesheets),
|
Some(ua_stylesheets),
|
||||||
&mut extra_data,
|
|
||||||
Some(element),
|
Some(element),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,13 @@ use gecko_bindings::structs::{StyleSheetInfo, ServoStyleSheetInner};
|
||||||
use gecko_bindings::structs::nsIDocument;
|
use gecko_bindings::structs::nsIDocument;
|
||||||
use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
|
use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||||
use invalidation::media_queries::{MediaListKey, ToMediaListKey};
|
use invalidation::media_queries::{MediaListKey, ToMediaListKey};
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::MallocSizeOfOps;
|
||||||
use media_queries::{Device, MediaList};
|
use media_queries::{Device, MediaList};
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
|
use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
|
||||||
use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
|
use stylesheets::{StylesheetContents, StylesheetInDocument};
|
||||||
use stylist::{ExtraStyleData, Stylist};
|
use stylist::Stylist;
|
||||||
|
|
||||||
/// Little wrapper to a Gecko style sheet.
|
/// Little wrapper to a Gecko style sheet.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -113,9 +113,6 @@ impl StylesheetInDocument for GeckoStyleSheet {
|
||||||
pub struct PerDocumentStyleDataImpl {
|
pub struct PerDocumentStyleDataImpl {
|
||||||
/// Rule processor.
|
/// Rule processor.
|
||||||
pub stylist: Stylist,
|
pub stylist: Stylist,
|
||||||
|
|
||||||
/// List of effective @font-face and @counter-style rules.
|
|
||||||
pub extra_style_data: PerOrigin<ExtraStyleData>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
||||||
|
@ -132,7 +129,6 @@ impl PerDocumentStyleData {
|
||||||
|
|
||||||
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
|
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
|
||||||
stylist: Stylist::new(device, quirks_mode.into()),
|
stylist: Stylist::new(device, quirks_mode.into()),
|
||||||
extra_style_data: Default::default(),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +156,6 @@ impl PerDocumentStyleDataImpl {
|
||||||
self.stylist.flush(
|
self.stylist.flush(
|
||||||
&StylesheetGuards::same(guard),
|
&StylesheetGuards::same(guard),
|
||||||
/* ua_sheets = */ None,
|
/* ua_sheets = */ None,
|
||||||
&mut self.extra_style_data,
|
|
||||||
document_element,
|
document_element,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -189,7 +184,6 @@ impl PerDocumentStyleDataImpl {
|
||||||
/// Measure heap usage.
|
/// Measure heap usage.
|
||||||
pub fn add_size_of_children(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
pub fn add_size_of_children(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||||
self.stylist.add_size_of_children(ops, sizes);
|
self.stylist.add_size_of_children(ops, sizes);
|
||||||
sizes.mStylistOther += self.extra_style_data.size_of(ops);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,10 @@ impl DocumentCascadeData {
|
||||||
self.per_origin.iter_origins()
|
self.per_origin.iter_origins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iter_origins_rev(&self) -> PerOriginIter<CascadeData> {
|
||||||
|
self.per_origin.iter_origins_rev()
|
||||||
|
}
|
||||||
|
|
||||||
/// Rebuild the cascade data for the given document stylesheets, and
|
/// Rebuild the cascade data for the given document stylesheets, and
|
||||||
/// optionally with a set of user agent stylesheets. Returns Err(..)
|
/// optionally with a set of user agent stylesheets. Returns Err(..)
|
||||||
/// to signify OOM.
|
/// to signify OOM.
|
||||||
|
@ -95,7 +99,6 @@ impl DocumentCascadeData {
|
||||||
flusher: StylesheetFlusher<'a, 'b, S>,
|
flusher: StylesheetFlusher<'a, 'b, S>,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
ua_stylesheets: Option<&UserAgentStylesheets>,
|
ua_stylesheets: Option<&UserAgentStylesheets>,
|
||||||
extra_data: &mut PerOrigin<ExtraStyleData>,
|
|
||||||
) -> Result<(), FailedAllocationError>
|
) -> Result<(), FailedAllocationError>
|
||||||
where
|
where
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
|
@ -114,7 +117,6 @@ impl DocumentCascadeData {
|
||||||
self.precomputed_pseudo_element_decls.clear();
|
self.precomputed_pseudo_element_decls.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
extra_data.borrow_mut_for_origin(&origin).clear();
|
|
||||||
if validity == OriginValidity::CascadeInvalid {
|
if validity == OriginValidity::CascadeInvalid {
|
||||||
cascade_data.clear_cascade_data()
|
cascade_data.clear_cascade_data()
|
||||||
} else {
|
} else {
|
||||||
|
@ -152,7 +154,6 @@ impl DocumentCascadeData {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
guards.ua_or_user,
|
guards.ua_or_user,
|
||||||
extra_data,
|
|
||||||
SheetRebuildKind::Full,
|
SheetRebuildKind::Full,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +182,6 @@ impl DocumentCascadeData {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
&ua_stylesheets.quirks_mode_stylesheet,
|
&ua_stylesheets.quirks_mode_stylesheet,
|
||||||
guards.ua_or_user,
|
guards.ua_or_user,
|
||||||
extra_data,
|
|
||||||
SheetRebuildKind::Full,
|
SheetRebuildKind::Full,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,6 @@ impl DocumentCascadeData {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
guards.author,
|
guards.author,
|
||||||
extra_data,
|
|
||||||
rebuild_kind,
|
rebuild_kind,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +208,6 @@ impl DocumentCascadeData {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
stylesheet: &S,
|
stylesheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
_extra_data: &mut PerOrigin<ExtraStyleData>,
|
|
||||||
rebuild_kind: SheetRebuildKind,
|
rebuild_kind: SheetRebuildKind,
|
||||||
) -> Result<(), FailedAllocationError>
|
) -> Result<(), FailedAllocationError>
|
||||||
where
|
where
|
||||||
|
@ -345,26 +343,26 @@ impl DocumentCascadeData {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::FontFace(ref rule) => {
|
CssRule::FontFace(ref rule) => {
|
||||||
_extra_data
|
origin_cascade_data
|
||||||
.borrow_mut_for_origin(&origin)
|
.extra_data
|
||||||
.add_font_face(rule);
|
.add_font_face(rule);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::FontFeatureValues(ref rule) => {
|
CssRule::FontFeatureValues(ref rule) => {
|
||||||
_extra_data
|
origin_cascade_data
|
||||||
.borrow_mut_for_origin(&origin)
|
.extra_data
|
||||||
.add_font_feature_values(rule);
|
.add_font_feature_values(rule);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::CounterStyle(ref rule) => {
|
CssRule::CounterStyle(ref rule) => {
|
||||||
_extra_data
|
origin_cascade_data
|
||||||
.borrow_mut_for_origin(&origin)
|
.extra_data
|
||||||
.add_counter_style(guard, rule);
|
.add_counter_style(guard, rule);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::Page(ref rule) => {
|
CssRule::Page(ref rule) => {
|
||||||
_extra_data
|
origin_cascade_data
|
||||||
.borrow_mut_for_origin(&origin)
|
.extra_data
|
||||||
.add_page(rule);
|
.add_page(rule);
|
||||||
}
|
}
|
||||||
// We don't care about any other rule.
|
// We don't care about any other rule.
|
||||||
|
@ -500,6 +498,18 @@ impl Stylist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate over the extra data in origin order.
|
||||||
|
#[inline]
|
||||||
|
pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
|
||||||
|
ExtraStyleDataIterator(self.cascade_data.iter_origins())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the extra data in reverse origin order.
|
||||||
|
#[inline]
|
||||||
|
pub fn iter_extra_data_origins_rev(&self) -> ExtraStyleDataIterator {
|
||||||
|
ExtraStyleDataIterator(self.cascade_data.iter_origins_rev())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of selectors.
|
/// Returns the number of selectors.
|
||||||
pub fn num_selectors(&self) -> usize {
|
pub fn num_selectors(&self) -> usize {
|
||||||
self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
|
self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
|
||||||
|
@ -548,7 +558,6 @@ impl Stylist {
|
||||||
&mut self,
|
&mut self,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
ua_sheets: Option<&UserAgentStylesheets>,
|
ua_sheets: Option<&UserAgentStylesheets>,
|
||||||
extra_data: &mut PerOrigin<ExtraStyleData>,
|
|
||||||
document_element: Option<E>,
|
document_element: Option<E>,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
|
@ -604,7 +613,6 @@ impl Stylist {
|
||||||
flusher,
|
flusher,
|
||||||
guards,
|
guards,
|
||||||
ua_sheets,
|
ua_sheets,
|
||||||
extra_data,
|
|
||||||
).unwrap_or_else(|_| warn!("OOM in Stylist::flush"));
|
).unwrap_or_else(|_| warn!("OOM in Stylist::flush"));
|
||||||
|
|
||||||
had_invalidations
|
had_invalidations
|
||||||
|
@ -1647,7 +1655,8 @@ impl Stylist {
|
||||||
|
|
||||||
/// This struct holds data which users of Stylist may want to extract
|
/// This struct holds data which users of Stylist may want to extract
|
||||||
/// from stylesheets which can be done at the same time as updating.
|
/// from stylesheets which can be done at the same time as updating.
|
||||||
#[derive(Default)]
|
#[derive(Debug, Default)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct ExtraStyleData {
|
pub struct ExtraStyleData {
|
||||||
/// A list of effective font-face rules and their origin.
|
/// A list of effective font-face rules and their origin.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -1666,6 +1675,11 @@ pub struct ExtraStyleData {
|
||||||
pub pages: Vec<Arc<Locked<PageRule>>>,
|
pub pages: Vec<Arc<Locked<PageRule>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(emilio): This is kind of a lie, and relies on us not cloning
|
||||||
|
// nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't).
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
unsafe impl Sync for ExtraStyleData {}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl ExtraStyleData {
|
impl ExtraStyleData {
|
||||||
/// Add the given @font-face rule.
|
/// Add the given @font-face rule.
|
||||||
|
@ -1706,6 +1720,18 @@ impl ExtraStyleData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator over the different ExtraStyleData.
|
||||||
|
pub struct ExtraStyleDataIterator<'a>(PerOriginIter<'a, CascadeData>);
|
||||||
|
|
||||||
|
impl<'a> Iterator for ExtraStyleDataIterator<'a> {
|
||||||
|
type Item = (&'a ExtraStyleData, Origin);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next().map(|d| (&d.0.extra_data, d.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl MallocSizeOf for ExtraStyleData {
|
impl MallocSizeOf for ExtraStyleData {
|
||||||
/// Measure heap usage.
|
/// Measure heap usage.
|
||||||
|
@ -1951,6 +1977,9 @@ struct CascadeData {
|
||||||
/// Effective media query results cached from the last rebuild.
|
/// Effective media query results cached from the last rebuild.
|
||||||
effective_media_query_results: EffectiveMediaQueryResults,
|
effective_media_query_results: EffectiveMediaQueryResults,
|
||||||
|
|
||||||
|
/// Extra data, like different kinds of rules, etc.
|
||||||
|
extra_data: ExtraStyleData,
|
||||||
|
|
||||||
/// A monotonically increasing counter to represent the order on which a
|
/// A monotonically increasing counter to represent the order on which a
|
||||||
/// style rule appears in a stylesheet, needed to sort them by source order.
|
/// style rule appears in a stylesheet, needed to sort them by source order.
|
||||||
rules_source_order: u32,
|
rules_source_order: u32,
|
||||||
|
@ -1968,6 +1997,7 @@ impl CascadeData {
|
||||||
element_map: SelectorMap::new(),
|
element_map: SelectorMap::new(),
|
||||||
pseudos_map: PerPseudoElementMap::default(),
|
pseudos_map: PerPseudoElementMap::default(),
|
||||||
animations: Default::default(),
|
animations: Default::default(),
|
||||||
|
extra_data: ExtraStyleData::default(),
|
||||||
invalidation_map: InvalidationMap::new(),
|
invalidation_map: InvalidationMap::new(),
|
||||||
attribute_dependencies: NonCountingBloomFilter::new(),
|
attribute_dependencies: NonCountingBloomFilter::new(),
|
||||||
style_attribute_dependency: false,
|
style_attribute_dependency: false,
|
||||||
|
@ -1998,6 +2028,7 @@ impl CascadeData {
|
||||||
self.element_map.clear();
|
self.element_map.clear();
|
||||||
self.pseudos_map.clear();
|
self.pseudos_map.clear();
|
||||||
self.animations.clear();
|
self.animations.clear();
|
||||||
|
self.extra_data.clear();
|
||||||
self.rules_source_order = 0;
|
self.rules_source_order = 0;
|
||||||
self.num_selectors = 0;
|
self.num_selectors = 0;
|
||||||
self.num_declarations = 0;
|
self.num_declarations = 0;
|
||||||
|
@ -2032,6 +2063,7 @@ impl CascadeData {
|
||||||
sizes.mStylistRevalidationSelectors += self.selectors_for_cache_revalidation.size_of(ops);
|
sizes.mStylistRevalidationSelectors += self.selectors_for_cache_revalidation.size_of(ops);
|
||||||
|
|
||||||
sizes.mStylistOther += self.effective_media_query_results.size_of(ops);
|
sizes.mStylistOther += self.effective_media_query_results.size_of(ops);
|
||||||
|
sizes.mStylistOther += self.extra_data.size_of(ops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1685,7 +1685,7 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
|
||||||
let page_decls = match pseudo {
|
let page_decls = match pseudo {
|
||||||
PseudoElement::PageContent => {
|
PseudoElement::PageContent => {
|
||||||
let mut declarations = vec![];
|
let mut declarations = vec![];
|
||||||
let iter = data.extra_style_data.iter_origins_rev();
|
let iter = data.stylist.iter_extra_data_origins_rev();
|
||||||
for (data, origin) in iter {
|
for (data, origin) in iter {
|
||||||
let level = match origin {
|
let level = match origin {
|
||||||
Origin::UserAgent => CascadeLevel::UANormal,
|
Origin::UserAgent => CascadeLevel::UANormal,
|
||||||
|
@ -3610,15 +3610,17 @@ pub extern "C" fn Servo_StyleSet_GetFontFaceRules(raw_data: RawServoStyleSetBorr
|
||||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
|
|
||||||
let len: u32 = data.extra_style_data
|
let len: u32 = data
|
||||||
.iter_origins()
|
.stylist
|
||||||
|
.iter_extra_data_origins()
|
||||||
.map(|(d, _)| d.font_faces.len() as u32)
|
.map(|(d, _)| d.font_faces.len() as u32)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
// Reversed iterator because Gecko expects rules to appear sorted
|
// Reversed iterator because Gecko expects rules to appear sorted
|
||||||
// UserAgent first, Author last.
|
// UserAgent first, Author last.
|
||||||
let font_face_iter = data.extra_style_data
|
let font_face_iter = data
|
||||||
.iter_origins_rev()
|
.stylist
|
||||||
|
.iter_extra_data_origins_rev()
|
||||||
.flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
|
.flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
|
||||||
|
|
||||||
unsafe { rules.set_len(len) };
|
unsafe { rules.set_len(len) };
|
||||||
|
@ -3632,12 +3634,11 @@ pub extern "C" fn Servo_StyleSet_GetFontFaceRules(raw_data: RawServoStyleSetBorr
|
||||||
pub extern "C" fn Servo_StyleSet_GetCounterStyleRule(raw_data: RawServoStyleSetBorrowed,
|
pub extern "C" fn Servo_StyleSet_GetCounterStyleRule(raw_data: RawServoStyleSetBorrowed,
|
||||||
name: *mut nsIAtom) -> *mut nsCSSCounterStyleRule {
|
name: *mut nsIAtom) -> *mut nsCSSCounterStyleRule {
|
||||||
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
|
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
|
||||||
let extra_data = &data.extra_style_data;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
Atom::with(name, |name| {
|
Atom::with(name, |name| {
|
||||||
extra_data
|
data.stylist
|
||||||
.iter_origins()
|
.iter_extra_data_origins()
|
||||||
.filter_map(|(d, _)| d.counter_styles.get(name))
|
.filter_map(|(d, _)| d.counter_styles.get(name))
|
||||||
.next()
|
.next()
|
||||||
})
|
})
|
||||||
|
@ -3656,17 +3657,19 @@ pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
|
||||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||||
let guard = global_style_data.shared_lock.read();
|
let guard = global_style_data.shared_lock.read();
|
||||||
|
|
||||||
let has_rule = data.extra_style_data
|
let has_rule =
|
||||||
.iter_origins()
|
data.stylist
|
||||||
.any(|(d, _)| !d.font_feature_values.is_empty());
|
.iter_extra_data_origins()
|
||||||
|
.any(|(d, _)| !d.font_feature_values.is_empty());
|
||||||
|
|
||||||
if !has_rule {
|
if !has_rule {
|
||||||
return ptr::null_mut();
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
let font_feature_values_iter = data.extra_style_data
|
let font_feature_values_iter =
|
||||||
.iter_origins_rev()
|
data.stylist
|
||||||
.flat_map(|(d, _)| d.font_feature_values.iter());
|
.iter_extra_data_origins_rev()
|
||||||
|
.flat_map(|(d, _)| d.font_feature_values.iter());
|
||||||
|
|
||||||
let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
|
let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
|
||||||
for src in font_feature_values_iter {
|
for src in font_feature_values_iter {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue