/* 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
from data import Keyword
%>
<%namespace name="helpers" file="/helpers.mako.rs" />
usecrate::Atom; use app_units::Au; usecrate::computed_value_flags::*; usecrate::custom_properties::ComputedCustomProperties; 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; usecrate::gecko_bindings::structs::mozilla::PseudoStyleType; usecrate::gecko::data::PerDocumentStyleData; usecrate::logical_geometry::WritingMode; usecrate::media_queries::Device; 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::{BorderStyle, Time, Zoom}; usecrate::values::computed::font::FontSize;
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
})
}
}
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(
custom_properties: ComputedCustomProperties,
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 { Self {
custom_properties,
writing_mode,
rules,
visited_style: visited_style.map_or(ptr::null(), |p| Arc::into_raw(p)) as *const _,
flags,
effective_zoom,
% for style_struct in data.style_structs:
${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
% endfor
}
}
fn to_outer(self, pseudo: Option<<&PseudoElement>) -> Arc<ComputedValues> { let pseudo_ty = match pseudo {
Some(p) => p.pseudo_type(),
None => structs::PseudoStyleType::NotPseudo,
}; unsafe { letmut arc = UniqueArc::<ComputedValues>::new_uninit();
bindings::Gecko_ComputedStyle_Init(
arc.as_mut_ptr() as *mut _,
&self,
pseudo_ty,
); // 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})
}
</%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
}
<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) for x in SIDES for y in ["style", "width"]]) %>
<%self:impl_trait style_struct_name="Border"
skip_longhands="${skip_border_longhands}">
% for side in SIDES: pubfn set_border_${side.ident}_style(&mutself, v: BorderStyle) { self.mBorderStyle[${side.index}] = v;
// This is needed because the initial mComputedBorder value is set to // zero. // // In order to compute stuff, we start from the initial struct, and keep // going down the tree applying properties. // // That means, effectively, that when we set border-style to something // non-hidden, we should use the initial border instead. // // Servo stores the initial border-width in the initial struct, and then // adjusts as needed in the fixup phase. This means that the initial // struct is technically not valid without fixups, and that you lose // pretty much any sharing of the initial struct, which is kind of // unfortunate. // // Gecko has two fields for this, one that stores the "specified" // border, and other that stores the actual computed one. That means // that when we set border-style, border-width may change and we need to // sync back to the specified one. This is what this function does. // // Note that this doesn't impose any dependency in the order of // computation of the properties. This is only relevant if border-style // is specified, but border-width isn't. If border-width is specified at // some point, the two mBorder and mComputedBorder fields would be the // same already. // // Once we're here, we know that we'll run style fixups, so it's fine to // just copy the specified border here, we'll adjust it if it's // incorrect later. self.mComputedBorder.${side.ident} = self.mBorder.${side.ident};
}
pubfn set_outline_style(&mutself, v: longhands::outline_style::computed_value::T) { self.mOutlineStyle = v; // NB: This is needed to correctly handling the initial value of // outline-width when outline-style changes, see the // update_border_${side.ident} comment for more details. self.mActualOutlineWidth = self.mOutlineWidth;
}
// 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} = { match servo {
% for value in keyword.values_for("gecko"):
Keyword::${to_camel_case(value)} =>
structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
% endfor
}
};
}
}
pubfn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { usecrate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
% if keyword.needs_cast():
% for value in keyword.values_for('gecko'): const ${keyword.casted_constant_name(value, "u8")} : u8 =
structs::${keyword.gecko_constant(value)} as u8;
% endfor
% 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| { match layer.${field_name} {
% for value in longhand.keyword.values_for("gecko"):
% if keyword.needs_cast():
${keyword.casted_constant_name(value, "u8")}
% else:
structs::${keyword.gecko_constant(value)}
% endif
=> Keyword::${to_camel_case(value)},
% endfor
% if keyword.gecko_inexhaustive:
_ => panic!("Found unexpected value in style struct for ${ident} property"),
% 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}">
<%self:impl_trait style_struct_name="Column"
skip_longhands="column-rule-width column-rule-style"> pubfn set_column_rule_style(&mutself, v: longhands::column_rule_style::computed_value::T) { self.mColumnRuleStyle = v; // NB: This is needed to correctly handling the initial value of // column-rule-width when colun-rule-style changes, see the // update_border_${side.ident} comment for more details. self.mActualColumnRuleWidth = self.mColumnRuleWidth;
}
/// 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
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-06-27)
¤
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.