/* 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/. */
usecrate::computed_value_flags::ComputedValueFlags; usecrate::dom::TElement; usecrate::logical_geometry::{LogicalSize, WritingMode}; usecrate::parser::ParserContext; usecrate::properties::ComputedValues; usecrate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; usecrate::queries::values::Orientation; usecrate::queries::{FeatureType, QueryCondition}; usecrate::shared_lock::{
DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
}; usecrate::str::CssStringWriter; usecrate::stylesheets::CssRules; usecrate::stylist::Stylist; usecrate::values::computed::{CSSPixelLength, ContainerType, Context, Ratio}; usecrate::values::specified::ContainerName; use app_units::Au; use cssparser::{Parser, SourceLocation}; use euclid::default::Size2D; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use selectors::kleene_value::KleeneValue; use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, ToCss};
/// A container rule. #[derive(Debug, ToShmem)] pubstruct ContainerRule { /// The container query and name. pub condition: Arc<ContainerCondition>, /// The nested rules inside the block. pub rules: Arc<Locked<CssRules>>, /// The source position where this rule was found. pub source_location: SourceLocation,
}
/// The result of a successful container query lookup. pubstruct ContainerLookupResult<E> { /// The relevant container. pub element: E, /// The sizing / writing-mode information of the container. pub info: ContainerInfo, /// The style of the element. pub style: Arc<ComputedValues>,
}
impl ContainerCondition { /// Parse a container condition. pubfn parse<'a>(
context: &ParserContext,
input: &mut Parser<'a, '_>,
) -> Result<Self, ParseError<'a>> { let name = input
.try_parse(|input| ContainerName::parse_for_query(context, input))
.ok()
.unwrap_or_else(ContainerName::none); let condition = QueryCondition::parse(context, input, FeatureType::Container)?; let flags = condition.cumulative_flags();
Ok(Self {
name,
condition,
flags,
})
}
fn valid_container_info<E>(
&self,
potential_container: E,
originating_element_style: Option<&ComputedValues>,
) -> TraversalResult<ContainerLookupResult<E>> where
E: TElement,
{ let data; let style = match originating_element_style {
Some(s) => s,
None => {
data = match potential_container.borrow_data() {
Some(d) => d,
None => return TraversalResult::InProgress,
};
&**data.styles.primary()
},
}; let wm = style.writing_mode; let box_style = style.get_box();
// Filter by container-type. let container_type = box_style.clone_container_type(); let available_axes = container_type_axes(container_type, wm); if !available_axes.contains(self.flags.container_axes()) { return TraversalResult::InProgress;
}
// Filter by container-name. let container_name = box_style.clone_container_name(); for filter_name inself.name.0.iter() { if !container_name.0.contains(filter_name) { return TraversalResult::InProgress;
}
}
/// Performs container lookup for a given element. pubfn find_container<E>(
&self,
e: E,
originating_element_style: Option<&ComputedValues>,
) -> Option<ContainerLookupResult<E>> where
E: TElement,
{ match traverse_container(
e,
originating_element_style,
|element, originating_element_style| { self.valid_container_info(element, originating_element_style)
},
) {
Some((_, result)) => Some(result),
None => None,
}
}
/// Tries to match a container query condition for a given element. pub(crate) fn matches<E>(
&self,
stylist: &Stylist,
element: E,
originating_element_style: Option<&ComputedValues>,
invalidation_flags: &mut ComputedValueFlags,
) -> KleeneValue where
E: TElement,
{ let result = self.find_container(element, originating_element_style); let (container, info) = match result {
Some(r) => (Some(r.element), Some((r.info, r.style))),
None => (None, None),
}; // Set up the lookup for the container in question, as the condition may be using container // query lengths. let size_query_container_lookup = ContainerSizeQuery::for_option_element(
container, /* known_parent_style = */ None, /* is_pseudo = */ false,
);
Context::for_container_query_evaluation(
stylist.device(),
Some(stylist),
info,
size_query_container_lookup,
|context| { let matches = self.condition.matches(context); if context
.style()
.flags()
.contains(ComputedValueFlags::USES_VIEWPORT_UNITS)
{ // TODO(emilio): Might need something similar to improve // invalidation of font relative container-query lengths.
invalidation_flags
.insert(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES);
}
matches
},
)
}
}
/// Information needed to evaluate an individual container query. #[derive(Copy, Clone)] pubstruct ContainerInfo {
size: Size2D<Option<Au>>,
wm: WritingMode,
}
/// https://drafts.csswg.org/css-contain-3/#container-features /// /// TODO: Support style queries, perhaps. pubstatic CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
Evaluator::OptionalLength(eval_width),
FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS,
),
feature!(
atom!("height"),
AllowsRanges::Yes,
Evaluator::OptionalLength(eval_height),
FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS,
),
feature!(
atom!("inline-size"),
AllowsRanges::Yes,
Evaluator::OptionalLength(eval_inline_size),
FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS,
),
feature!(
atom!("block-size"),
AllowsRanges::Yes,
Evaluator::OptionalLength(eval_block_size),
FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS,
),
feature!(
atom!("aspect-ratio"),
AllowsRanges::Yes,
Evaluator::OptionalNumberRatio(eval_aspect_ratio), // XXX from_bits_truncate is const, but the pipe operator isn't, so this // works around it.
FeatureFlags::from_bits_truncate(
FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() |
FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()
),
),
feature!(
atom!("orientation"),
AllowsRanges::No,
keyword_evaluator!(eval_orientation, Orientation),
FeatureFlags::from_bits_truncate(
FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() |
FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()
),
),
];
/// Result of a container size query, signifying the hypothetical containment boundary in terms of physical axes. /// Defined by up to two size containers. Queries on logical axes are resolved with respect to the querying /// element's writing mode. #[derive(Copy, Clone, Default)] pubstruct ContainerSizeQueryResult {
width: Option<Au>,
height: Option<Au>,
}
/// Get the inline-size of the query container. pubfn get_container_inline_size(&self, context: &Context) -> Au { if context.builder.writing_mode.is_horizontal() { iflet Some(w) = self.width { return w;
}
} else { iflet Some(h) = self.height { return h;
}
} Self::get_logical_viewport_size(context).inline
}
/// Get the block-size of the query container. pubfn get_container_block_size(&self, context: &Context) -> Au { if context.builder.writing_mode.is_horizontal() { self.get_container_height(context)
} else { self.get_container_width(context)
}
}
/// Get the width of the query container. pubfn get_container_width(&self, context: &Context) -> Au { iflet Some(w) = self.width { return w;
} Self::get_viewport_size(context).width
}
/// Get the height of the query container. pubfn get_container_height(&self, context: &Context) -> Au { iflet Some(h) = self.height { return h;
} Self::get_viewport_size(context).height
}
// Merge the result of a subsequent lookup, preferring the initial result. fn merge(self, new_result: Self) -> Self { letmut result = self; iflet Some(width) = new_result.width {
result.width.get_or_insert(width);
} iflet Some(height) = new_result.height {
result.height.get_or_insert(height);
}
result
}
/// Find the query container size for a given element. Meant to be used as a callback for new(). fn lookup<E>(
element: E,
originating_element_style: Option<&ComputedValues>,
) -> ContainerSizeQueryResult where
E: TElement + 'a,
{ match traverse_container(
element,
originating_element_style,
|e, originating_element_style| { Self::evaluate_potential_size_container(e, originating_element_style)
},
) {
Some((container, result)) => { if result.is_complete() {
result
} else { // Traverse up from the found size container to see if we can get a complete containment.
result.merge(Self::lookup(container, None))
}
},
None => ContainerSizeQueryResult::default(),
}
}
/// Create a new instance of the container size query for given element, with a deferred lookup callback. pubfn for_element<E>(
element: E,
known_parent_style: Option<&'a ComputedValues>,
is_pseudo: bool,
) -> Self where
E: TElement + 'a,
{ let parent; let data; let parent_style = match known_parent_style {
Some(s) => Some(s),
None => { // No need to bother if we're the top element.
parent = match element.traversal_parent() {
Some(parent) => parent,
None => returnSelf::none(),
};
data = parent.borrow_data();
data.as_ref().map(|data| &**data.styles.primary())
},
};
// If there's no style, such as being `display: none` or so, we still want to show a // correct computed value, so give it a try. let should_traverse = parent_style.map_or(true, |s| {
s.flags
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
}); if !should_traverse { returnSelf::none();
} returnSelf::NotEvaluated(Box::new(move || { Self::lookup(element, if is_pseudo { known_parent_style } else { None })
}));
}
/// Create a new instance, but with optional element. pubfn for_option_element<E>(
element: Option<E>,
known_parent_style: Option<&'a ComputedValues>,
is_pseudo: bool,
) -> Self where
E: TElement + 'a,
{ iflet Some(e) = element { Self::for_element(e, known_parent_style, is_pseudo)
} else { Self::none()
}
}
/// Create a query that evaluates to empty, for cases where container size query is not required. pubfn none() -> Self {
ContainerSizeQuery::Evaluated(ContainerSizeQueryResult::default())
}
/// Get the result of the container size query, doing the lookup if called for the first time. pubfn get(&mutself) -> ContainerSizeQueryResult { matchself { Self::NotEvaluated(lookup) => {
*self = Self::Evaluated((lookup)()); matchself { Self::Evaluated(info) => *info,
_ => unreachable!("Just evaluated but not set?"),
}
}, Self::Evaluated(info) => *info,
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-18)
¤
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.