/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// `data` comes from components/style/properties.mako.rs; see build.rs for more details.
<%! from data import to_camel_case, to_camel_case_lower, Keyword, SYSTEM_FONT_LONGHANDS %>
<%namespace name="helpers" file="/helpers.mako.rs" />
usecrate::Atom; usecrate::logical_geometry::PhysicalSide; usecrate::computed_value_flags::*; usecrate::custom_properties::ComputedCustomProperties; usecrate::device::Device; usecrate::gecko_bindings::bindings;
% for style_struct in data.style_structs: usecrate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name}; usecrate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name}; usecrate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endfor usecrate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; usecrate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; usecrate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; usecrate::gecko_bindings::structs::{self, PseudoStyleType}; usecrate::gecko::data::PerDocumentStyleData; usecrate::logical_geometry::WritingMode; usecrate::properties::longhands; usecrate::rule_tree::StrongRuleNode; usecrate::selector_parser::PseudoElement; use servo_arc::{Arc, UniqueArc}; use std::mem::{forget, MaybeUninit, ManuallyDrop}; use std::{ops, ptr}; usecrate::values; usecrate::values::computed::{Time, Zoom}; usecrate::values::computed::font::FontSize; usecrate::dom::AttributeReferences;
pubmod style_structs {
% for style_struct in data.style_structs: pubusesuper::${style_struct.gecko_struct_name} as ${style_struct.name};
unsafeimpl Send for ${style_struct.name} {} unsafeimpl Sync for ${style_struct.name} {}
% endfor
}
/// FIXME(emilio): This is completely duplicated with the other properties code. pubtype ComputedValuesInner = structs::ServoComputedData;
/// Converts the computed values to an Arc<> from a reference. pubfn to_arc(&self) -> Arc<Self> { // SAFETY: We're guaranteed to be allocated as an Arc<> since the // functions above are the only ones that create ComputedValues // instances in Gecko (and that must be the case since ComputedValues' // member is private). unsafe { Arc::from_raw_addrefed(self) }
}
/// Returns true if the display property is changed from 'none' to others. pubfn is_display_property_changed_from_none(
&self,
old_values: Option<&ComputedValues>
) -> bool { usecrate::properties::longhands::display::computed_value::T as Display;
old_values.map_or(false, |old| { let old_display_style = old.get_box().clone_display(); let new_display_style = self.get_box().clone_display();
old_display_style == Display::None &&
new_display_style != Display::None
})
}
/// Calls the given function for each cached lazy pseudo-element style. pubfn each_cached_lazy_pseudo<F>(&self, mut f: F) where
F: FnMut(&Self),
{
thin_vec::auto_thin_vec!(let array: [*const structs::ComputedStyle; 4]); unsafe {
bindings::Gecko_GetCachedLazyPseudoStyles( self.as_gecko_computed_style(),
array.as_mut().as_mut_ptr(),
);
} for style in array.iter() { // ComputedValues is a newtype around ComputedStyle, so same layout let values: &ComputedValues = unsafe { &*(*style as *const ComputedValues) };
f(values);
}
}
}
impl Drop for ComputedValues { fn drop(&mutself) { // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side, // that's pretty wild. unsafe {
bindings::Gecko_ComputedStyle_Destroy(&mutself.0);
}
}
}
unsafeimpl Sync for ComputedValues {} unsafeimpl Send for ComputedValues {}
impl Drop for ComputedValuesInner { fn drop(&mutself) {
% for style_struct in data.style_structs: let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };
% endfor if !self.visited_style.is_null() { let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };
}
}
}
impl ComputedValuesInner { pubfn new(
pseudo: Option<&PseudoElement>,
custom_properties: ComputedCustomProperties,
attribute_references: AttributeReferences,
writing_mode: WritingMode,
effective_zoom: Zoom,
flags: ComputedValueFlags,
rules: Option<StrongRuleNode>,
visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self { let pseudo_type = match pseudo {
Some(p) => p.pseudo_type(),
None => PseudoStyleType::NotPseudo,
}; Self {
custom_properties,
attribute_references,
writing_mode,
rules,
visited_style: visited_style.map_or(ptr::null(), Arc::into_raw) as *const _,
flags,
pseudo_type,
effective_zoom,
% for style_struct in data.style_structs:
${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
% endfor
}
}
// Share ComputedValues but with different flags. pubfn clone_with_flags(&self, flags: ComputedValueFlags, pseudo: Option<&PseudoElement>) -> Arc<ComputedValues> { Self::new(
pseudo, self.custom_properties.clone(), self.attribute_references.clone(), self.writing_mode.clone(), self.effective_zoom.clone(),
flags, self.rules.clone(), ifself.visited_style.is_null() {
None
} else {
Some(unsafe { Arc::from_raw_addrefed(self.visited_style as *const _) })
},
% for style_struct in data.style_structs: unsafe { Arc::from_raw_addrefed(self.${style_struct.gecko_name} as *const _) },
% endfor
).to_outer()
}
fn to_outer(self) -> Arc<ComputedValues> { unsafe { letmut arc = UniqueArc::<ComputedValues>::new_uninit();
bindings::Gecko_ComputedStyle_Init(
arc.as_mut_ptr() as *mut _,
&self
); // We're simulating move semantics by having C++ do a memcpy and // then forgetting it on this end.
forget(self);
UniqueArc::assume_init(arc).shareable()
}
}
}
impl ops::Deref for ComputedValues { type Target = ComputedValuesInner; #[inline] fn deref(&self) -> &ComputedValuesInner {
&self.0.mSource
}
}
impl ComputedValuesInner { /// Returns true if the value of the `content` property would make a /// pseudo-element not rendered. #[inline] pubfn ineffective_content_property(&self) -> bool { self.get_counters().ineffective_content_property()
}
/// Returns the visited style, if any. pubfn visited_style(&self) -> Option<&ComputedValues> { unsafe { self.visited_style_ptr().as_ref() }
}
% for style_struct in data.style_structs: #[inline] fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} { // This is sound because the wrapper we create is repr(transparent). self.${style_struct.gecko_name} as *const _
}
<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pubfn set_${ident}(&mutself, v: longhands::${ident}::computed_value::T) { usecrate::properties::longhands::${ident}::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts let result = match v {
% for value in keyword.values_for('gecko'):
Keyword::${to_camel_case(value)} =>
structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
% endfor
};
${set_gecko_property(gecko_ffi_name, "result")}
}
</%def>
<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pubfn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { usecrate::properties::longhands::${ident}::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
// Some constant macros in the gecko are defined as negative integer(e.g. font-stretch). // And they are convert to signed integer in Rust bindings. We need to cast then // as signed type when we have both signed/unsigned integer in order to use them // as match's arms. // Also, to use same implementation here we use casted constant if we have only singed values.
% if keyword.gecko_enum_prefix is None:
% for value in keyword.values_for('gecko'): const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
structs::${keyword.gecko_constant(value)} as ${cast_type};
% endfor
match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
% for value in keyword.values_for('gecko'):
${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
% endfor
% if keyword.gecko_inexhaustive:
_ => panic!("Found unexpected value in style struct for ${ident} property"),
% endif
}
% else: match ${get_gecko_property(gecko_ffi_name)} {
% for value in keyword.values_for('gecko'):
structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
% endfor
% if keyword.gecko_inexhaustive:
_ => panic!("Found unexpected value in style struct for ${ident} property"),
% endif
}
% endif
}
</%def>
#[allow(non_snake_case)] pubfn copy_${ident}_from(&mutself, other: &Self) { self.${inherit_from} = other.${inherit_from}; // NOTE: This is needed to easily handle the `unset` and `initial` // keywords, which are implemented calling this function. // // In practice, this means that we may have an incorrect value here, but // we'll adjust that properly in the style fixup phase. // // FIXME(emilio): We could clean this up a bit special-casing the reset_ // function below. self.${gecko_ffi_name} = other.${inherit_from};
}
#[allow(non_snake_case)] pubfn clone_${ident}(&self) -> Au {
Au(self.${gecko_ffi_name})
}
${impl_simple_eq(ident, gecko_ffi_name)}
</%def>
<%def name="impl_style_struct(style_struct)"> /// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct / /// clone it. #[repr(transparent)] pubstruct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);
impl ops::Deref for ${style_struct.gecko_struct_name} { type Target = structs::${style_struct.gecko_ffi_name}; #[inline] fn deref(&self) -> &Self::Target {
&self.0
}
}
<%def name="impl_trait(style_struct_name, skip_longhands='')">
<%
style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
longhands = [x for x in style_struct.longhands if not (skip_longhands == "*" or x.name in skip_longhands.split())]
if longhand.logical: return # get the method and pass additional keyword or type-specific arguments if longhand.keyword:
method = impl_keyword
args.update(keyword=longhand.keyword) if"font"in longhand.ident:
args.update(cast_type=longhand.cast_type) else:
method = impl_simple
#[allow(dead_code)] fn static_assert() { // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
% for side in SIDES:
{ const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
% endfor
}
// Negative numbers are invalid at parse time, but <integer> is still an // i32.
<% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
<% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
self.mSize = other.mScriptUnconstrainedSize; // NOTE: Intentionally not copying from mFont.size. The cascade process // recomputes the used size as needed. self.mFont.size = other.mSize; self.mFontSizeKeyword = other.mFontSizeKeyword;
// TODO(emilio): Should we really copy over these two? self.mFontSizeFactor = other.mFontSizeFactor; self.mFontSizeOffset = other.mFontSizeOffset;
}
// These two may be changed from Cascade::fixup_font_stuff. self.mSize = computed_size; // NOTE: Intentionally not copying from used_size. The cascade process // recomputes the used size as needed. self.mFont.size = computed_size;
pubfn set_${shorthand}_${name}<I>(&mutself, v: I) where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{ usecrate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter();
let count = other.${layers_field_name}.${field_name}Count; unsafe {
Gecko_EnsureImageLayersLength(&mutself.${layers_field_name},
count as usize,
LayerType::${shorthand.title()});
} // FIXME(emilio): This may be bogus in the same way as bug 1426246. for (layer, other) inself.${layers_field_name}.mLayers.iter_mut()
.zip(other.${layers_field_name}.mLayers.iter())
.take(count as usize) {
layer.${field_name} = other.${field_name}.clone();
} self.${layers_field_name}.${field_name}Count = count;
}
<%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
<%
ident = "%s_%s" % (shorthand, name)
style_struct = next(x for x in data.style_structs if x.name == struct_name)
longhand = next(x for x in style_struct.longhands if x.ident == ident)
keyword = longhand.keyword
%>
self.${layer_field_name}.${field_name}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {
geckolayer.${field_name} = {
% if keyword: match servo {
% for value in keyword.values_for("gecko"):
Keyword::${to_camel_case(value)} =>
structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
% endfor
}
% else: // The Gecko field stores the computed value directly.
servo
% endif
};
}
}
${impl_fallback_eq(ident)}
pubfn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
% if keyword: usecrate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
% endif
longhands::${ident}::computed_value::List( self.${layer_field_name}.mLayers.iter()
.take(self.${layer_field_name}.${field_name}Count as usize)
.map(|ref layer| {
% if keyword: match layer.${field_name} {
% for value in longhand.keyword.values_for("gecko"):
structs::${keyword.gecko_constant(value)}
=> Keyword::${to_camel_case(value)},
% endfor
% if keyword.gecko_inexhaustive:
_ => panic!("Found unexpected value in style struct for ${ident} property"),
% endif
}
% else:
layer.${field_name}
% endif
}).collect()
)
}
</%def>
<%
fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize" if shorthand == "background":
fill_fields += " mAttachment mBlendMode" else: # mSourceURI uses mImageCount
fill_fields += " mMaskMode mComposite"
%> pubfn fill_arrays(&mutself) { usecrate::gecko_bindings::bindings::Gecko_FillAllImageLayers; use std::cmp; letmut max_len = 1;
% for member in fill_fields.split():
max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);
% endfor unsafe { // While we could do this manually, we'd need to also manually // run all the copy constructors, so we just delegate to gecko
Gecko_FillAllImageLayers(&mutself.${image_layers_field}, max_len);
}
}
</%def>
// TODO: Gecko accepts lists in most background-related properties. We just use // the first element (which is the common case), but at some point we want to // add support for parsing these lists in servo and pushing to nsTArray's.
<% skip_background_longhands = """background-repeat
background-image background-clip
background-origin background-attachment
background-size background-position
background-blend-mode
background-position-x
background-position-y""" %>
<%self:impl_trait style_struct_name="Background"
skip_longhands="${skip_background_longhands}">
/// Returns whether there are any transitions specified. pubfn specifies_transitions(&self) -> bool { ifself.mTransitionPropertyCount == 1 && self.transition_combined_duration_at(0).seconds() <= 0.0f32 { returnfalse;
} self.mTransitionPropertyCount > 0
}
/// Returns whether animation-timeline is initial value. We need this information to resolve /// animation-duration. pubfn has_initial_animation_timeline(&self) -> bool { self.mAnimationTimelineCount == 1 && self.animation_timeline_at(0).is_auto()
}
% for style_struct in data.style_structs:
${impl_style_struct(style_struct)}
% endfor
/// Assert that the initial values set in Gecko style struct constructors /// match the values returned by `get_initial_value()` for each longhand. #[cfg(feature = "gecko")] #[inline] pubfn assert_initial_values_match(data: &PerDocumentStyleData) { if cfg!(debug_assertions) { let data = data.borrow(); let cv = data.stylist.device().default_computed_values();
<% # Skip properties with initial values that change at computed # value time, or whose initial value depends on the document # / other prefs.
SKIPPED = [ "border-top-width", "border-bottom-width", "border-left-width", "border-right-width", "column-rule-width", "font-family", "font-size", "outline-width", "color",
]
TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
%>
% for property in TO_TEST:
assert_eq!(
cv.clone_${property.ident}(),
longhands::${property.ident}::get_initial_value(),
concat!( "initial value in Gecko style struct for ",
stringify!(${property.ident}), " must match longhands::",
stringify!(${property.ident}), "::get_initial_value()"
)
);
% endfor
}
}
% if engine == "gecko": pubmod system_font { //! We deal with system fonts here //! //! System fonts can only be set as a group via the font shorthand. //! They resolve at compute time (not parse time -- this lets the //! browser respond to changes to the OS font settings). //! //! While Gecko handles these as a separate property and keyword //! values on each property indicating that the font should be picked //! from the -x-system-font property, we avoid this. Instead, //! each font longhand has a special SystemFont variant which contains //! the specified system font. When the cascade function (in helpers) //! detects that a value has a system font, it will resolve it, and //! cache it on the ComputedValues. After this, it can be just fetched //! whenever a font longhand on the same element needs the system font. //! //! When a longhand property is holding a SystemFont, it's serialized //! to an empty string as if its value comes from a shorthand with //! variable reference. We may want to improve this behavior at some //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
usecrate::properties::longhands; use std::hash::{Hash, Hasher}; usecrate::values::computed::{ToComputedValue, Context}; usecrate::values::specified::font::SystemFont; // ComputedValues are compared at times // so we need these impls. We don't want to // add Eq to Number (which contains a float) // so instead we have an eq impl which skips the // cached values impl PartialEq for ComputedSystemFont { fn eq(&self, other: &Self) -> bool { self.system_font == other.system_font
}
} impl Eq for ComputedSystemFont {}
#[inline] /// Compute and cache a system font /// /// Must be called before attempting to compute a system font /// specified value pubfn resolve_system_font(system: SystemFont, context: &mut Context) { // Checking if context.cached_system_font.is_none() isn't enough, // if animating from one system font to another the cached system font // may change if context.cached_system_font.as_ref().is_none_or(|x| x.system_font != system) { let computed = system.to_computed_value(context);
context.cached_system_font = Some(computed);
}
}
#[derive(Clone, Debug)] pubstruct ComputedSystemFont {
% for name in SYSTEM_FONT_LONGHANDS: pub ${name}: longhands::${name}::computed_value::T,
% endfor pub system_font: SystemFont,
}
}
% endif
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.