mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
style: Fix elements not being query containers for the pseudo-element they originate. r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D164807
This commit is contained in:
parent
c511b9c3f9
commit
661e7d5c48
5 changed files with 129 additions and 50 deletions
|
@ -8,6 +8,7 @@ use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::gecko_bindings::structs::RawServoSelectorList;
|
use crate::gecko_bindings::structs::RawServoSelectorList;
|
||||||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||||
use crate::invalidation::element::document_state::InvalidationMatchingData;
|
use crate::invalidation::element::document_state::InvalidationMatchingData;
|
||||||
|
use crate::properties::ComputedValues;
|
||||||
use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
|
use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
|
||||||
use crate::str::starts_with_ignore_ascii_case;
|
use crate::str::starts_with_ignore_ascii_case;
|
||||||
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||||
|
@ -17,6 +18,7 @@ use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
|
||||||
use dom::{DocumentState, ElementState};
|
use dom::{DocumentState, ElementState};
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use selectors::SelectorList;
|
use selectors::SelectorList;
|
||||||
|
use servo_arc::Arc;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
||||||
|
|
||||||
|
@ -233,7 +235,7 @@ pub struct SelectorImpl;
|
||||||
|
|
||||||
/// A set of extra data to carry along with the matching context, either for
|
/// A set of extra data to carry along with the matching context, either for
|
||||||
/// selector-matching or invalidation.
|
/// selector-matching or invalidation.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Default)]
|
||||||
pub struct ExtraMatchingData {
|
pub struct ExtraMatchingData {
|
||||||
/// The invalidation data to invalidate doc-state pseudo-classes correctly.
|
/// The invalidation data to invalidate doc-state pseudo-classes correctly.
|
||||||
pub invalidation_data: InvalidationMatchingData,
|
pub invalidation_data: InvalidationMatchingData,
|
||||||
|
@ -241,6 +243,10 @@ pub struct ExtraMatchingData {
|
||||||
/// The invalidation bits from matching container queries. These are here
|
/// The invalidation bits from matching container queries. These are here
|
||||||
/// just for convenience mostly.
|
/// just for convenience mostly.
|
||||||
pub cascade_input_flags: ComputedValueFlags,
|
pub cascade_input_flags: ComputedValueFlags,
|
||||||
|
|
||||||
|
/// The style of the originating element in order to evaluate @container
|
||||||
|
/// size queries affecting pseudo-elements.
|
||||||
|
pub originating_element_style: Option<Arc<ComputedValues>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
|
|
|
@ -281,7 +281,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
|
let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
|
||||||
let container_size_query = ContainerSizeQuery::for_option_element(element);
|
let container_size_query = ContainerSizeQuery::for_option_element(element, None);
|
||||||
|
|
||||||
let mut context = computed::Context::new(
|
let mut context = computed::Context::new(
|
||||||
// We'd really like to own the rules here to avoid refcount traffic, but
|
// We'd really like to own the rules here to avoid refcount traffic, but
|
||||||
|
|
|
@ -420,7 +420,7 @@ where
|
||||||
layout_parent_style: Option<&ComputedValues>,
|
layout_parent_style: Option<&ComputedValues>,
|
||||||
) -> Option<ResolvedStyle> {
|
) -> Option<ResolvedStyle> {
|
||||||
let MatchingResults { rule_node, mut flags } = self.match_pseudo(
|
let MatchingResults { rule_node, mut flags } = self.match_pseudo(
|
||||||
originating_element_style.style(),
|
&originating_element_style.style.0,
|
||||||
pseudo,
|
pseudo,
|
||||||
VisitedHandlingMode::AllLinksUnvisited,
|
VisitedHandlingMode::AllLinksUnvisited,
|
||||||
)?;
|
)?;
|
||||||
|
@ -428,7 +428,7 @@ where
|
||||||
let mut visited_rules = None;
|
let mut visited_rules = None;
|
||||||
if originating_element_style.style().visited_style().is_some() {
|
if originating_element_style.style().visited_style().is_some() {
|
||||||
visited_rules = self.match_pseudo(
|
visited_rules = self.match_pseudo(
|
||||||
originating_element_style.style(),
|
&originating_element_style.style.0,
|
||||||
pseudo,
|
pseudo,
|
||||||
VisitedHandlingMode::RelevantLinkVisited,
|
VisitedHandlingMode::RelevantLinkVisited,
|
||||||
).map(|results| {
|
).map(|results| {
|
||||||
|
@ -506,7 +506,7 @@ where
|
||||||
|
|
||||||
fn match_pseudo(
|
fn match_pseudo(
|
||||||
&mut self,
|
&mut self,
|
||||||
originating_element_style: &ComputedValues,
|
originating_element_style: &Arc<ComputedValues>,
|
||||||
pseudo_element: &PseudoElement,
|
pseudo_element: &PseudoElement,
|
||||||
visited_handling: VisitedHandlingMode,
|
visited_handling: VisitedHandlingMode,
|
||||||
) -> Option<MatchingResults> {
|
) -> Option<MatchingResults> {
|
||||||
|
@ -534,7 +534,7 @@ where
|
||||||
let bloom_filter = self.context.thread_local.bloom_filter.filter();
|
let bloom_filter = self.context.thread_local.bloom_filter.filter();
|
||||||
let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
|
let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
|
||||||
|
|
||||||
let mut matching_context = MatchingContext::new_for_visited(
|
let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
|
||||||
MatchingMode::ForStatelessPseudoElement,
|
MatchingMode::ForStatelessPseudoElement,
|
||||||
Some(bloom_filter),
|
Some(bloom_filter),
|
||||||
Some(nth_index_cache),
|
Some(nth_index_cache),
|
||||||
|
@ -542,6 +542,8 @@ where
|
||||||
self.context.shared.quirks_mode(),
|
self.context.shared.quirks_mode(),
|
||||||
NeedsSelectorFlags::Yes,
|
NeedsSelectorFlags::Yes,
|
||||||
);
|
);
|
||||||
|
matching_context.extra_data.originating_element_style =
|
||||||
|
Some(originating_element_style.clone());
|
||||||
|
|
||||||
// NB: We handle animation rules for ::before and ::after when
|
// NB: We handle animation rules for ::before and ::after when
|
||||||
// traversing them.
|
// traversing them.
|
||||||
|
|
|
@ -135,15 +135,26 @@ enum TraversalResult<T> {
|
||||||
Done(T),
|
Done(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse_container<E, F, R>(mut e: E, evaluator: F) -> Option<(E, R)>
|
fn traverse_container<E, F, R>(
|
||||||
|
mut e: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
|
evaluator: F,
|
||||||
|
) -> Option<(E, R)>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
F: Fn(E) -> TraversalResult<R>,
|
F: Fn(E, Option<&Arc<ComputedValues>>) -> TraversalResult<R>,
|
||||||
{
|
{
|
||||||
while let Some(element) = e.traversal_parent() {
|
if originating_element_style.is_some() {
|
||||||
match evaluator(element) {
|
match evaluator(e, originating_element_style) {
|
||||||
TraversalResult::InProgress => {},
|
TraversalResult::InProgress => {},
|
||||||
TraversalResult::StopTraversal => break,
|
TraversalResult::StopTraversal => return None,
|
||||||
|
TraversalResult::Done(result) => return Some((e, result)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(element) = e.traversal_parent() {
|
||||||
|
match evaluator(element, None) {
|
||||||
|
TraversalResult::InProgress => {},
|
||||||
|
TraversalResult::StopTraversal => return None,
|
||||||
TraversalResult::Done(result) => return Some((element, result)),
|
TraversalResult::Done(result) => return Some((element, result)),
|
||||||
}
|
}
|
||||||
e = element;
|
e = element;
|
||||||
|
@ -174,15 +185,22 @@ impl ContainerCondition {
|
||||||
fn valid_container_info<E>(
|
fn valid_container_info<E>(
|
||||||
&self,
|
&self,
|
||||||
potential_container: E,
|
potential_container: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
) -> TraversalResult<ContainerLookupResult<E>>
|
) -> TraversalResult<ContainerLookupResult<E>>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
let data = match potential_container.borrow_data() {
|
let data;
|
||||||
Some(data) => data,
|
let style = match originating_element_style {
|
||||||
None => return TraversalResult::InProgress,
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
data = match potential_container.borrow_data() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => return TraversalResult::InProgress,
|
||||||
|
};
|
||||||
|
data.styles.primary()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let style = data.styles.primary();
|
|
||||||
let wm = style.writing_mode;
|
let wm = style.writing_mode;
|
||||||
let box_style = style.get_box();
|
let box_style = style.get_box();
|
||||||
|
|
||||||
|
@ -211,11 +229,21 @@ impl ContainerCondition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs container lookup for a given element.
|
/// Performs container lookup for a given element.
|
||||||
pub fn find_container<E>(&self, e: E) -> Option<ContainerLookupResult<E>>
|
pub fn find_container<E>(
|
||||||
|
&self,
|
||||||
|
e: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
|
) -> Option<ContainerLookupResult<E>>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
match traverse_container(e, |element| self.valid_container_info(element)) {
|
match traverse_container(
|
||||||
|
e,
|
||||||
|
originating_element_style,
|
||||||
|
|element, originating_element_style| {
|
||||||
|
self.valid_container_info(element, originating_element_style)
|
||||||
|
},
|
||||||
|
) {
|
||||||
Some((_, result)) => Some(result),
|
Some((_, result)) => Some(result),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
|
@ -226,18 +254,19 @@ impl ContainerCondition {
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
element: E,
|
element: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
invalidation_flags: &mut ComputedValueFlags,
|
invalidation_flags: &mut ComputedValueFlags,
|
||||||
) -> KleeneValue
|
) -> KleeneValue
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
let result = self.find_container(element);
|
let result = self.find_container(element, originating_element_style);
|
||||||
let (container, info) = match result {
|
let (container, info) = match result {
|
||||||
Some(r) => (Some(r.element), Some((r.info, r.style))),
|
Some(r) => (Some(r.element), Some((r.info, r.style))),
|
||||||
None => (None, None),
|
None => (None, None),
|
||||||
};
|
};
|
||||||
// Set up the lookup for the container in question, as the condition may be using container query lengths.
|
// 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);
|
let size_query_container_lookup = ContainerSizeQuery::for_option_element(container, None);
|
||||||
Context::for_container_query_evaluation(
|
Context::for_container_query_evaluation(
|
||||||
device,
|
device,
|
||||||
info,
|
info,
|
||||||
|
@ -448,16 +477,24 @@ pub enum ContainerSizeQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContainerSizeQuery<'a> {
|
impl<'a> ContainerSizeQuery<'a> {
|
||||||
fn evaluate_potential_size_container<E>(e: E) -> TraversalResult<ContainerSizeQueryResult>
|
fn evaluate_potential_size_container<E>(
|
||||||
|
e: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
|
) -> TraversalResult<ContainerSizeQueryResult>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
let data = match e.borrow_data() {
|
let data;
|
||||||
Some(data) => data,
|
let style = match originating_element_style {
|
||||||
None => return TraversalResult::InProgress,
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
data = match e.borrow_data() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => return TraversalResult::InProgress,
|
||||||
|
};
|
||||||
|
data.styles.primary()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let style = data.styles.primary();
|
|
||||||
if !style
|
if !style
|
||||||
.flags
|
.flags
|
||||||
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
|
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
|
||||||
|
@ -494,17 +531,26 @@ impl<'a> ContainerSizeQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the query container size for a given element. Meant to be used as a callback for new().
|
/// Find the query container size for a given element. Meant to be used as a callback for new().
|
||||||
fn lookup<E>(element: E) -> ContainerSizeQueryResult
|
fn lookup<E>(
|
||||||
|
element: E,
|
||||||
|
originating_element_style: Option<&Arc<ComputedValues>>,
|
||||||
|
) -> ContainerSizeQueryResult
|
||||||
where
|
where
|
||||||
E: TElement + 'a,
|
E: TElement + 'a,
|
||||||
{
|
{
|
||||||
match traverse_container(element, |e| Self::evaluate_potential_size_container(e)) {
|
match traverse_container(
|
||||||
|
element,
|
||||||
|
originating_element_style,
|
||||||
|
|e, originating_element_style| {
|
||||||
|
Self::evaluate_potential_size_container(e, originating_element_style)
|
||||||
|
},
|
||||||
|
) {
|
||||||
Some((container, result)) => {
|
Some((container, result)) => {
|
||||||
if result.is_complete() {
|
if result.is_complete() {
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
// Traverse up from the found size container to see if we can get a complete containment.
|
// Traverse up from the found size container to see if we can get a complete containment.
|
||||||
result.merge(Self::lookup(container))
|
result.merge(Self::lookup(container, None))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => ContainerSizeQueryResult::default(),
|
None => ContainerSizeQueryResult::default(),
|
||||||
|
@ -512,35 +558,51 @@ impl<'a> ContainerSizeQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance of the container size query for given element, with a deferred lookup callback.
|
/// Create a new instance of the container size query for given element, with a deferred lookup callback.
|
||||||
pub fn for_element<E>(element: E) -> Self
|
pub fn for_element<E>(
|
||||||
|
element: E,
|
||||||
|
originating_element_style: Option<&'a Arc<ComputedValues>>,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
E: TElement + 'a,
|
E: TElement + 'a,
|
||||||
{
|
{
|
||||||
// No need to bother if we're the top element.
|
let parent;
|
||||||
if let Some(parent) = element.traversal_parent() {
|
let data;
|
||||||
let should_traverse = match parent.borrow_data() {
|
let style = match originating_element_style {
|
||||||
Some(data) => {
|
Some(s) => Some(s),
|
||||||
let style = data.styles.primary();
|
None => {
|
||||||
style
|
// No need to bother if we're the top element.
|
||||||
.flags
|
parent = match element.traversal_parent() {
|
||||||
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
|
Some(parent) => parent,
|
||||||
},
|
None => return Self::none(),
|
||||||
None => true, // `display: none`, still want to show a correct computed value, so give it a try.
|
};
|
||||||
};
|
data = parent.borrow_data();
|
||||||
if should_traverse {
|
data.as_ref().map(|data| data.styles.primary())
|
||||||
return Self::NotEvaluated(Box::new(move || Self::lookup(element)));
|
},
|
||||||
}
|
};
|
||||||
|
let should_traverse = match style {
|
||||||
|
Some(style) => style
|
||||||
|
.flags
|
||||||
|
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE),
|
||||||
|
None => true, // `display: none`, still want to show a correct computed value, so give it a try.
|
||||||
|
};
|
||||||
|
if should_traverse {
|
||||||
|
return Self::NotEvaluated(Box::new(move || {
|
||||||
|
Self::lookup(element, originating_element_style)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
Self::none()
|
Self::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance, but with optional element.
|
/// Create a new instance, but with optional element.
|
||||||
pub fn for_option_element<E>(element: Option<E>) -> Self
|
pub fn for_option_element<E>(
|
||||||
|
element: Option<E>,
|
||||||
|
originating_element_style: Option<&'a Arc<ComputedValues>>,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
E: TElement + 'a,
|
E: TElement + 'a,
|
||||||
{
|
{
|
||||||
if let Some(e) = element {
|
if let Some(e) = element {
|
||||||
Self::for_element(e)
|
Self::for_element(e, originating_element_style)
|
||||||
} else {
|
} else {
|
||||||
Self::none()
|
Self::none()
|
||||||
}
|
}
|
||||||
|
|
|
@ -966,7 +966,7 @@ impl Stylist {
|
||||||
element: E,
|
element: E,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &Arc<ComputedValues>,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
|
matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
|
||||||
) -> Option<Arc<ComputedValues>>
|
) -> Option<Arc<ComputedValues>>
|
||||||
|
@ -1105,7 +1105,7 @@ impl Stylist {
|
||||||
&self,
|
&self,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
element: E,
|
element: E,
|
||||||
parent_style: &ComputedValues,
|
parent_style: &Arc<ComputedValues>,
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
is_probe: bool,
|
is_probe: bool,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
|
@ -1125,7 +1125,7 @@ impl Stylist {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut declarations = ApplicableDeclarationList::new();
|
let mut declarations = ApplicableDeclarationList::new();
|
||||||
let mut matching_context = MatchingContext::new(
|
let mut matching_context = MatchingContext::<'_, E::Impl>::new(
|
||||||
MatchingMode::ForStatelessPseudoElement,
|
MatchingMode::ForStatelessPseudoElement,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
@ -1134,6 +1134,7 @@ impl Stylist {
|
||||||
);
|
);
|
||||||
|
|
||||||
matching_context.pseudo_element_matching_fn = matching_fn;
|
matching_context.pseudo_element_matching_fn = matching_fn;
|
||||||
|
matching_context.extra_data.originating_element_style = Some(parent_style.clone());
|
||||||
|
|
||||||
self.push_applicable_declarations(
|
self.push_applicable_declarations(
|
||||||
element,
|
element,
|
||||||
|
@ -1155,7 +1156,7 @@ impl Stylist {
|
||||||
let mut visited_rules = None;
|
let mut visited_rules = None;
|
||||||
if parent_style.visited_style().is_some() {
|
if parent_style.visited_style().is_some() {
|
||||||
let mut declarations = ApplicableDeclarationList::new();
|
let mut declarations = ApplicableDeclarationList::new();
|
||||||
let mut matching_context = MatchingContext::new_for_visited(
|
let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
|
||||||
MatchingMode::ForStatelessPseudoElement,
|
MatchingMode::ForStatelessPseudoElement,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
@ -1164,6 +1165,7 @@ impl Stylist {
|
||||||
needs_selector_flags,
|
needs_selector_flags,
|
||||||
);
|
);
|
||||||
matching_context.pseudo_element_matching_fn = matching_fn;
|
matching_context.pseudo_element_matching_fn = matching_fn;
|
||||||
|
matching_context.extra_data.originating_element_style = Some(parent_style.clone());
|
||||||
|
|
||||||
self.push_applicable_declarations(
|
self.push_applicable_declarations(
|
||||||
element,
|
element,
|
||||||
|
@ -2380,7 +2382,14 @@ impl CascadeData {
|
||||||
None => return true,
|
None => return true,
|
||||||
Some(ref c) => c,
|
Some(ref c) => c,
|
||||||
};
|
};
|
||||||
let matches = condition.matches(stylist.device(), element, &mut context.extra_data.cascade_input_flags).to_bool(/* unknown = */ false);
|
let matches = condition
|
||||||
|
.matches(
|
||||||
|
stylist.device(),
|
||||||
|
element,
|
||||||
|
context.extra_data.originating_element_style.as_ref(),
|
||||||
|
&mut context.extra_data.cascade_input_flags,
|
||||||
|
)
|
||||||
|
.to_bool(/* unknown = */ false);
|
||||||
if !matches {
|
if !matches {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue