Bug 1792501: Part 5 - :has DOM mutation invalidation. r=emilio,layout-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D185678
This commit is contained in:
David Shin
2023-09-14 22:21:25 +00:00
parent 14291fa2ca
commit 9f1cc6d0c9
27 changed files with 615 additions and 1515 deletions

View File

@@ -80,6 +80,9 @@ RestyleManager::RestyleManager(nsPresContext* aPresContext)
void RestyleManager::ContentInserted(nsIContent* aChild) {
MOZ_ASSERT(aChild->GetParentNode());
if (aChild->IsElement()) {
StyleSet()->MaybeInvalidateForElementInsertion(*aChild->AsElement());
}
RestyleForInsertOrChange(aChild);
}
@@ -95,6 +98,10 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) {
}
}
#endif
if (aFirstNewContent->IsElement()) {
StyleSet()->MaybeInvalidateForElementAppend(*aFirstNewContent->AsElement());
}
const auto selectorFlags = container->GetSelectorFlags() &
NodeSelectorFlags::AllSimpleRestyleFlagsForAppend;
if (!selectorFlags) {
@@ -412,6 +419,19 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild,
IncrementUndisplayedRestyleGeneration();
}
if (aOldChild->IsElement()) {
Element* nextSibling =
aFollowingSibling ? aFollowingSibling->IsElement()
? aFollowingSibling->AsElement()
: aFollowingSibling->GetNextElementSibling()
: nullptr;
Element* prevSibling = aFollowingSibling
? aFollowingSibling->GetPreviousElementSibling()
: container->GetLastElementChild();
StyleSet()->MaybeInvalidateForElementRemove(*aOldChild->AsElement(),
prevSibling, nextSibling);
}
const auto selectorFlags =
container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
if (!selectorFlags) {

View File

@@ -1408,6 +1408,24 @@ void ServoStyleSet::MaybeInvalidateRelativeSelectorStateDependency(
mRawData.get(), &aElement, aState.GetInternalValue());
}
void ServoStyleSet::MaybeInvalidateForElementInsertion(
const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(mRawData.get(),
&aElement);
}
void ServoStyleSet::MaybeInvalidateForElementAppend(const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(mRawData.get(),
&aElement);
}
void ServoStyleSet::MaybeInvalidateForElementRemove(
const Element& aElement, const Element* aPrevSibling,
const Element* aNextSibling) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
mRawData.get(), &aElement, aPrevSibling, aNextSibling);
}
bool ServoStyleSet::MightHaveNthOfAttributeDependency(
const Element& aElement, nsAtom* aAttribute) const {
return Servo_StyleSet_MightHaveNthOfAttributeDependency(

View File

@@ -490,6 +490,26 @@ class ServoStyleSet {
void MaybeInvalidateRelativeSelectorStateDependency(const dom::Element&,
dom::ElementState);
/**
* Maybe invalidate if a DOM element insertion might require us to restyle
* the relative selector to ancestors/previous siblings.
*/
void MaybeInvalidateForElementInsertion(const dom::Element&);
/**
* Maybe invalidate if a DOM element append might require us to restyle
* the relative selector to ancestors/previous siblings.
*/
void MaybeInvalidateForElementAppend(const dom::Element&);
/**
* Maybe invalidate if a DOM element removal might require us to restyle
* the relative selector to ancestors/previous siblings.
*/
void MaybeInvalidateForElementRemove(const dom::Element& aElement,
const dom::Element* aPrevSibling,
const dom::Element* aNextSibling);
/**
* Returns true if a change in event state on an element might require
* us to restyle the element.

View File

@@ -187,6 +187,32 @@ impl Dependency {
}
DependencyInvalidationKind::Normal(self.normal_invalidation_kind())
}
/// Is the combinator to the right of this dependency's compound selector
/// the next sibling combinator? This matters for insertion/removal in between
/// two elements connected through next sibling, e.g. `.foo:has(> .a + .b)`
/// where an element gets inserted between `.a` and `.b`.
pub fn right_combinator_is_next_sibling(&self) -> bool {
if self.selector_offset == 0 {
return false;
}
matches!(
self.selector.combinator_at_match_order(self.selector_offset - 1),
Combinator::NextSibling
)
}
/// Is this dependency's compound selector a single compound in `:has`
/// with the next sibling relative combinator i.e. `:has(> .foo)`?
/// This matters for insertion between an anchor and an element
/// connected through next sibling, e.g. `.a:has(> .b)`.
pub fn dependency_is_relative_with_single_next_sibling(&self) -> bool {
match self.invalidation_kind() {
DependencyInvalidationKind::Normal(_) => false,
DependencyInvalidationKind::Relative(kind) =>
kind == RelativeDependencyInvalidationKind::PrevSibling,
}
}
}
/// The same, but for state selectors, which can track more exactly what state
@@ -224,8 +250,8 @@ pub struct DocumentStateDependency {
pub type IdOrClassDependencyMap = MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>;
/// Dependency mapping for pseudo-class states.
pub type StateDependencyMap = SelectorMap<StateDependency>;
/// Dependency mapping for attributes.
pub type AttributeDependencyMap = PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>;
/// Dependency mapping for local names.
pub type LocalNameDependencyMap = PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>;
/// A map where we store invalidations.
///
@@ -248,7 +274,7 @@ pub struct InvalidationMap {
/// A list of document state dependencies in the rules we represent.
pub document_state_selectors: Vec<DocumentStateDependency>,
/// A map of other attribute affecting selectors.
pub other_attribute_affecting_selectors: AttributeDependencyMap,
pub other_attribute_affecting_selectors: LocalNameDependencyMap,
}
/// A map to store all relative selector invalidations.
@@ -256,6 +282,10 @@ pub struct InvalidationMap {
pub struct RelativeSelectorInvalidationMap {
/// Portion common to the normal invalidation map, except that this is for relative selectors and their inner selectors.
pub map: InvalidationMap,
/// A map from a given type name to all the relative selector dependencies with that type.
pub type_to_selector: LocalNameDependencyMap,
/// All relative selector dependencies that specify `*`.
pub any_to_selector: SmallVec<[Dependency; 1]>,
/// Flag indicating if any relative selector is used.
pub used: bool,
/// Flag indicating if invalidating a relative selector requires ancestor traversal.
@@ -267,6 +297,8 @@ impl RelativeSelectorInvalidationMap {
pub fn new() -> Self {
Self {
map: InvalidationMap::new(),
type_to_selector: LocalNameDependencyMap::default(),
any_to_selector: SmallVec::default(),
used: false,
needs_ancestors_traversal: false,
}
@@ -280,11 +312,14 @@ impl RelativeSelectorInvalidationMap {
/// Clears this map, leaving it empty.
pub fn clear(&mut self) {
self.map.clear();
self.type_to_selector.clear();
self.any_to_selector.clear();
}
/// Shrink the capacity of hash maps if needed.
pub fn shrink_if_needed(&mut self) {
self.map.shrink_if_needed();
self.type_to_selector.shrink_if_needed();
}
}
@@ -296,7 +331,7 @@ impl InvalidationMap {
id_to_selector: IdOrClassDependencyMap::new(),
state_affecting_selectors: StateDependencyMap::new(),
document_state_selectors: Vec::new(),
other_attribute_affecting_selectors: AttributeDependencyMap::default(),
other_attribute_affecting_selectors: LocalNameDependencyMap::default(),
}
}
@@ -402,7 +437,7 @@ trait Collector {
fn id_map(&mut self) -> &mut IdOrClassDependencyMap;
fn class_map(&mut self) -> &mut IdOrClassDependencyMap;
fn state_map(&mut self) -> &mut StateDependencyMap;
fn attribute_map(&mut self) -> &mut AttributeDependencyMap;
fn attribute_map(&mut self) -> &mut LocalNameDependencyMap;
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState);
}
@@ -440,6 +475,14 @@ fn on_id_or_class<C: Collector>(
fn add_attr_dependency<C: Collector>(name: LocalName, collector: &mut C) -> Result<(), AllocErr> {
let dependency = collector.dependency();
let map = collector.attribute_map();
add_local_name(name, dependency, map)
}
fn add_local_name(
name: LocalName,
dependency: Dependency,
map: &mut LocalNameDependencyMap,
) -> Result<(), AllocErr> {
map.try_reserve(1)?;
let vec = map.entry(name).or_default();
vec.try_reserve(1)?;
@@ -574,7 +617,7 @@ impl<'a> Collector for SelectorDependencyCollector<'a> {
&mut self.map.state_affecting_selectors
}
fn attribute_map(&mut self) -> &mut AttributeDependencyMap {
fn attribute_map(&mut self) -> &mut LocalNameDependencyMap {
&mut self.map.other_attribute_affecting_selectors
}
@@ -699,7 +742,7 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
combinator_count: RelativeSelectorCombinatorCount::new(relative_selector),
parent_selectors: &mut *self.parent_selectors,
quirks_mode: self.quirks_mode,
compound_state: PerCompoundState::new(0),
compound_state: RelativeSelectorPerCompoundState::new(0),
alloc_error: &mut *self.alloc_error,
};
if !nested.visit_whole_selector() {
@@ -744,6 +787,20 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
}
}
struct RelativeSelectorPerCompoundState {
state: PerCompoundState,
added_entry: bool,
}
impl RelativeSelectorPerCompoundState {
fn new(offset: usize) -> Self {
Self {
state: PerCompoundState::new(offset),
added_entry: false,
}
}
}
/// A struct that collects invalidations for a given compound selector.
struct RelativeSelectorDependencyCollector<'a> {
map: &'a mut RelativeSelectorInvalidationMap,
@@ -770,13 +827,48 @@ struct RelativeSelectorDependencyCollector<'a> {
quirks_mode: QuirksMode,
/// State relevant to a given compound selector.
compound_state: PerCompoundState,
compound_state: RelativeSelectorPerCompoundState,
/// The allocation error, if we OOM.
alloc_error: &'a mut Option<AllocErr>,
}
impl<'a> RelativeSelectorDependencyCollector<'a> {
fn add_non_unique_info(&mut self) -> Result<(), AllocErr> {
// Go through this compound again.
for ss in self
.selector
.selector
.iter_from(self.compound_state.state.offset)
{
match ss {
Component::LocalName(ref name) => {
let dependency = self.dependency();
add_local_name(
name.name.clone(),
dependency,
&mut self.map.type_to_selector,
)?;
if name.name != name.lower_name {
let dependency = self.dependency();
add_local_name(
name.lower_name.clone(),
dependency,
&mut self.map.type_to_selector,
)?;
}
return Ok(());
},
_ => (),
};
}
// Ouch. Add one for *.
self.map.any_to_selector.try_reserve(1)?;
let dependency = self.dependency();
self.map.any_to_selector.push(dependency);
Ok(())
}
fn visit_whole_selector(&mut self) -> bool {
let mut iter = self.selector.selector.iter_skip_relative_selector_anchor();
let mut index = 0;
@@ -789,7 +881,7 @@ impl<'a> RelativeSelectorDependencyCollector<'a> {
};
loop {
// Reset the compound state.
self.compound_state = PerCompoundState::new(index);
self.compound_state = RelativeSelectorPerCompoundState::new(index);
// Visit all the simple selectors in this sequence.
for ss in &mut iter {
@@ -800,7 +892,7 @@ impl<'a> RelativeSelectorDependencyCollector<'a> {
}
if let Err(err) = add_pseudo_class_dependency(
self.compound_state.element_state,
self.compound_state.state.element_state,
self.quirks_mode,
self,
) {
@@ -808,6 +900,14 @@ impl<'a> RelativeSelectorDependencyCollector<'a> {
return false;
}
if !self.compound_state.added_entry {
// Not great - we didn't add any uniquely identifiable information.
if let Err(err) = self.add_non_unique_info() {
*self.alloc_error = Some(err);
return false;
}
}
let combinator = iter.next_sequence();
if let Some(c) = combinator {
match c {
@@ -832,7 +932,7 @@ impl<'a> Collector for RelativeSelectorDependencyCollector<'a> {
let parent = parent_dependency(self.parent_selectors);
Dependency {
selector: self.selector.selector.clone(),
selector_offset: self.compound_state.offset,
selector_offset: self.compound_state.state.offset,
relative_kind: Some(match self.combinator_count.get_match_hint() {
RelativeSelectorMatchHint::InChild => RelativeDependencyInvalidationKind::Parent,
RelativeSelectorMatchHint::InSubtree => RelativeDependencyInvalidationKind::Ancestors,
@@ -865,12 +965,12 @@ impl<'a> Collector for RelativeSelectorDependencyCollector<'a> {
&mut self.map.map.state_affecting_selectors
}
fn attribute_map(&mut self) -> &mut AttributeDependencyMap {
fn attribute_map(&mut self) -> &mut LocalNameDependencyMap {
&mut self.map.map.other_attribute_affecting_selectors
}
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {
self.compound_state.element_state |= element_state;
self.compound_state.state.element_state |= element_state;
*self.document_state |= document_state;
}
}
@@ -897,6 +997,7 @@ impl<'a> SelectorVisitor for RelativeSelectorDependencyCollector<'a> {
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
match *s {
Component::ID(..) | Component::Class(..) => {
self.compound_state.added_entry = true;
if let Err(err) = on_id_or_class(s, self.quirks_mode, self) {
*self.alloc_error = Some(err.into());
return false;
@@ -904,12 +1005,20 @@ impl<'a> SelectorVisitor for RelativeSelectorDependencyCollector<'a> {
true
},
Component::NonTSPseudoClass(ref pc) => {
if !pc
.state_flag()
.intersects(ElementState::VISITED_OR_UNVISITED)
{
// Visited/Unvisited styling doesn't take the usual state invalidation path.
self.compound_state.added_entry = true;
}
if let Err(err) = on_pseudo_class(pc, self) {
*self.alloc_error = Some(err.into());
return false;
}
true
},
Component::RelativeSelectorAnchor => unreachable!("Should not visit this far"),
_ => true,
}
}
@@ -920,6 +1029,7 @@ impl<'a> SelectorVisitor for RelativeSelectorDependencyCollector<'a> {
local_name: &LocalName,
local_name_lower: &LocalName,
) -> bool {
self.compound_state.added_entry = true;
if let Err(err) = on_attribute(local_name, local_name_lower, self) {
*self.alloc_error = Some(err);
return false;

View File

@@ -5,15 +5,27 @@
//! Invalidation of element styles relative selectors.
use crate::data::ElementData;
use crate::dom::TElement;
use crate::dom::{TElement, TNode};
use crate::invalidation::element::invalidation_map::{
Dependency, DependencyInvalidationKind, RelativeDependencyInvalidationKind, NormalDependencyInvalidationKind, RelativeSelectorInvalidationMap,
};
use crate::invalidation::element::invalidator::{
DescendantInvalidationLists, Invalidation, InvalidationProcessor, InvalidationResult,
InvalidationVector, SiblingTraversalMap, TreeStyleInvalidator,
};
use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::invalidation::element::invalidator::{TreeStyleInvalidator, InvalidationResult, InvalidationProcessor, InvalidationVector, DescendantInvalidationLists, Invalidation, SiblingTraversalMap,};
use crate::invalidation::element::invalidation_map::{Dependency, RelativeDependencyInvalidationKind, DependencyInvalidationKind, NormalDependencyInvalidationKind};
use crate::invalidation::element::state_and_attributes::{invalidated_descendants, invalidated_self, invalidated_sibling, should_process_descendants, push_invalidation, dependency_may_be_relevant};
use crate::invalidation::element::state_and_attributes::{
dependency_may_be_relevant, invalidated_descendants, invalidated_self, invalidated_sibling,
push_invalidation, should_process_descendants,
};
use crate::stylist::{CascadeData, Stylist};
use dom::ElementState;
use fxhash::FxHashMap;
use selectors::matching::{
ElementSelectorFlags, MatchingContext, MatchingForInvalidation, MatchingMode,
NeedsSelectorFlags, QuirksMode, SelectorCaches, VisitedHandlingMode,
};
use selectors::OpaqueElement;
use selectors::matching::{QuirksMode, ElementSelectorFlags, MatchingContext, SelectorCaches, MatchingForInvalidation, MatchingMode, VisitedHandlingMode, NeedsSelectorFlags};
use selectors::parser::SelectorKey;
use smallvec::SmallVec;
use std::ops::DerefMut;
@@ -29,6 +41,8 @@ where
pub quirks_mode: QuirksMode,
/// Callback to trigger when the subject element is invalidated.
pub invalidated: fn(E, &InvalidationResult),
/// The traversal map that should be used to process invalidations.
pub sibling_traversal_map: SiblingTraversalMap<E>,
/// Marker for 'a lifetime.
pub _marker: ::std::marker::PhantomData<&'a ()>,
}
@@ -89,13 +103,16 @@ where
outer: &'a Dependency,
host: Option<E>,
) {
self.invalidations.entry(key).and_modify(|(h, o, d)| {
// Just keep one.
if *o <= offset {
return;
}
(*h, *o, *d) = (host, offset, outer);
}).or_insert_with(|| (host, offset, outer));
self.invalidations
.entry(key)
.and_modify(|(h, o, d)| {
// Just keep one.
if *o <= offset {
return;
}
(*h, *o, *d) = (host, offset, outer);
})
.or_insert_with(|| (host, offset, outer));
}
/// Add this dependency, if it is unique (i.e. Different outer dependency or same outer dependency
@@ -159,6 +176,87 @@ where
}
result
}
fn collect_all_dependencies_for_element(
&mut self,
element: E,
scope: &Option<E>,
quirks_mode: QuirksMode,
map: &'a RelativeSelectorInvalidationMap,
accept: fn(&Dependency) -> bool,
) {
element
.id()
.map(|v| match map.map.id_to_selector.get(v, quirks_mode) {
Some(v) => {
for dependency in v {
if !accept(dependency) {
continue;
}
self.add_dependency(dependency, element, *scope);
}
},
None => (),
});
element.each_class(|v| match map.map.class_to_selector.get(v, quirks_mode) {
Some(v) => {
for dependency in v {
if !accept(dependency) {
continue;
}
self.add_dependency(dependency, element, *scope);
}
},
None => (),
});
element.each_attr_name(
|v| match map.map.other_attribute_affecting_selectors.get(v) {
Some(v) => {
for dependency in v {
if !accept(dependency) {
continue;
}
self.add_dependency(dependency, element, *scope);
}
},
None => (),
},
);
let state = element.state();
map.map.state_affecting_selectors.lookup_with_additional(
element,
quirks_mode,
None,
&[],
ElementState::empty(),
|dependency| {
if !dependency.state.intersects(state) {
return true;
}
if !accept(&dependency.dep) {
return true;
}
self.add_dependency(&dependency.dep, element, *scope);
true
},
);
if let Some(v) = map.type_to_selector.get(element.local_name()) {
for dependency in v {
if !accept(dependency) {
continue;
}
self.add_dependency(dependency, element, *scope);
}
}
for dependency in &map.any_to_selector {
if !accept(dependency) {
continue;
}
self.add_dependency(dependency, element, *scope);
}
}
}
impl<'a, E> RelativeSelectorInvalidator<'a, E>
@@ -196,6 +294,45 @@ where
self.invalidate_from_dependencies(collector.get());
}
/// Gather relative selector dependencies for the given element (And its subtree) that mutated, and invalidate as necessary.
pub fn invalidate_relative_selectors_for_dom_mutation(
self,
subtree: bool,
stylist: &'a Stylist,
inherited_search_path: ElementSelectorFlags,
accept: fn(&Dependency) -> bool,
) {
let mut collector = RelativeSelectorDependencyCollector::<'a, E>::new(self.element);
let mut traverse_subtree = false;
self.element.apply_selector_flags(inherited_search_path);
stylist.for_each_cascade_data_with_scope(self.element, |data, scope| {
let map = data.relative_selector_invalidation_map();
if !map.used {
return;
}
traverse_subtree |= map.needs_ancestors_traversal;
collector.collect_all_dependencies_for_element(self.element, &scope, self.quirks_mode, map, accept);
});
if subtree && traverse_subtree {
for node in self.element.as_node().dom_descendants() {
let descendant = match node.as_element() {
Some(e) => e,
None => continue,
};
descendant.apply_selector_flags(inherited_search_path);
stylist.for_each_cascade_data_with_scope(descendant, |data, scope| {
let map = data.relative_selector_invalidation_map();
if !map.used {
return;
}
collector.collect_all_dependencies_for_element(descendant, &scope, self.quirks_mode, map, accept);
});
}
}
self.invalidate_from_dependencies(collector.get());
}
/// Carry out complete invalidation triggered by a relative selector invalidation.
/// Updates the relative selector search path if provided.
fn invalidate_from_dependencies(&self, to_invalidate: ToInvalidate<'a, E>) {
@@ -235,15 +372,17 @@ where
}
},
RelativeDependencyInvalidationKind::PrevSibling => {
self.element.prev_sibling_element().map(|e| {
if !Self::in_search_direction(
&e,
ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
) {
return;
}
self.handle_anchor(e, outer_dependency, host);
});
self.sibling_traversal_map
.prev_sibling_for(&self.element)
.map(|e| {
if !Self::in_search_direction(
&e,
ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
) {
return;
}
self.handle_anchor(e, outer_dependency, host);
});
},
RelativeDependencyInvalidationKind::AncestorPrevSibling => {
let mut parent = self.element.parent_element();
@@ -267,7 +406,7 @@ where
}
},
RelativeDependencyInvalidationKind::EarlierSibling => {
let mut sibling = self.element.prev_sibling_element();
let mut sibling = self.sibling_traversal_map.prev_sibling_for(&self.element);
while let Some(sib) = sibling {
if !Self::in_search_direction(
&sib,

View File

@@ -10,7 +10,7 @@ use cssparser::{Parser, ParserInput, SourceLocation, UnicodeRange};
use dom::{DocumentState, ElementState};
use malloc_size_of::MallocSizeOfOps;
use nsstring::{nsCString, nsString};
use selectors::matching::{MatchingForInvalidation, SelectorCaches};
use selectors::matching::{ElementSelectorFlags, MatchingForInvalidation, SelectorCaches};
use selectors::Element;
use servo_arc::{Arc, ArcBorrow};
use smallvec::SmallVec;
@@ -97,7 +97,7 @@ use style::global_style_data::{
GlobalStyleData, PlatformThreadHandle, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
};
use style::invalidation::element::invalidation_map::RelativeSelectorInvalidationMap;
use style::invalidation::element::invalidator::InvalidationResult;
use style::invalidation::element::invalidator::{InvalidationResult, SiblingTraversalMap};
use style::invalidation::element::relative_selector::{
RelativeSelectorDependencyCollector, RelativeSelectorInvalidator,
};
@@ -6783,6 +6783,28 @@ fn add_relative_selector_attribute_dependency<'a>(
};
}
fn inherit_relative_selector_search_direction(
element: &GeckoElement,
prev_sibling: Option<GeckoElement>,
) -> ElementSelectorFlags {
let mut inherited = ElementSelectorFlags::empty();
if let Some(parent) = element.parent_element() {
if let Some(direction) = parent.relative_selector_search_direction() {
inherited |= direction
.intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR);
}
}
if let Some(sibling) = prev_sibling {
if let Some(direction) = sibling.relative_selector_search_direction() {
// Inherit both, for e.g. a sibling with `:has(~.sibling .descendant)`
inherited |= direction.intersection(
ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING,
);
}
}
inherited
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
raw_data: &PerDocumentStyleData,
@@ -6798,6 +6820,7 @@ pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};
@@ -6838,6 +6861,7 @@ pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};
@@ -6881,6 +6905,7 @@ pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDepende
element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};
@@ -6919,6 +6944,7 @@ pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};
@@ -6940,6 +6966,201 @@ pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
);
}
fn invalidate_relative_selector_prev_sibling_side_effect(
prev_sibling: GeckoElement,
quirks_mode: QuirksMode,
sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
stylist: &Stylist,
) {
let invalidator = RelativeSelectorInvalidator {
element: prev_sibling,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map,
_marker: std::marker::PhantomData,
};
invalidator.invalidate_relative_selectors_for_dom_mutation(
false,
&stylist,
ElementSelectorFlags::empty(),
|d| d.right_combinator_is_next_sibling(),
);
}
fn invalidate_relative_selector_next_sibling_side_effect(
next_sibling: GeckoElement,
quirks_mode: QuirksMode,
sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
stylist: &Stylist,
) {
let invalidator = RelativeSelectorInvalidator {
element: next_sibling,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map,
_marker: std::marker::PhantomData,
};
invalidator.invalidate_relative_selectors_for_dom_mutation(
false,
&stylist,
ElementSelectorFlags::empty(),
|d| d.dependency_is_relative_with_single_next_sibling(),
);
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(
raw_data: &PerDocumentStyleData,
element: &RawGeckoElement,
) {
let element = GeckoElement(element);
let data = raw_data.borrow();
let quirks_mode: QuirksMode = data.stylist.quirks_mode();
let inherited =
inherit_relative_selector_search_direction(&element, element.prev_sibling_element());
if inherited.is_empty() {
return;
}
// Ok, we could've been inserted between two sibling elements that were connected
// through next sibling. This can happen in two ways:
// * `.a:has(+ .b)`
// * `:has(.. .a + .b ..)`
// Note that the previous sibling may be the anchor, and not part of the invalidation chain.
// Either way, there must be siblings to both sides of the element being inserted
// to consider it.
match (element.prev_sibling_element(), element.next_sibling_element()) {
(Some(prev_sibling), Some(next_sibling)) => {
invalidate_relative_selector_prev_sibling_side_effect(
prev_sibling,
quirks_mode,
SiblingTraversalMap::new(
prev_sibling,
prev_sibling.prev_sibling_element(),
element.next_sibling_element(),
), // Pretend this inserted element isn't here.
&data.stylist,
);
invalidate_relative_selector_next_sibling_side_effect(
next_sibling,
quirks_mode,
SiblingTraversalMap::new(
next_sibling,
Some(prev_sibling),
next_sibling.next_sibling_element(),
),
&data.stylist,
);
},
_ => (),
};
let invalidator = RelativeSelectorInvalidator {
element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};
invalidator.invalidate_relative_selectors_for_dom_mutation(
true,
&data.stylist,
inherited,
|_| true,
);
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(
raw_data: &PerDocumentStyleData,
first_element: &RawGeckoElement,
) {
let first_element = GeckoElement(first_element);
let data = raw_data.borrow();
let quirks_mode: QuirksMode = data.stylist.quirks_mode();
let inherited = inherit_relative_selector_search_direction(
&first_element,
first_element.prev_sibling_element(),
);
if inherited.is_empty() {
return;
}
let mut element = Some(first_element);
while let Some(e) = element {
let invalidator = RelativeSelectorInvalidator {
element: e,
quirks_mode,
sibling_traversal_map: SiblingTraversalMap::default(),
invalidated: relative_selector_invalidated_at,
_marker: std::marker::PhantomData,
};
invalidator.invalidate_relative_selectors_for_dom_mutation(
true,
&data.stylist,
inherited,
|_| true,
);
element = e.next_sibling_element();
}
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
raw_data: &PerDocumentStyleData,
element: &RawGeckoElement,
prev_sibling: Option<&RawGeckoElement>,
next_sibling: Option<&RawGeckoElement>,
) {
let element = GeckoElement(element);
let next_sibling = next_sibling.map(|e| GeckoElement(e));
let prev_sibling = prev_sibling.map(|e| GeckoElement(e));
let data = raw_data.borrow();
let quirks_mode: QuirksMode = data.stylist.quirks_mode();
let inherited = inherit_relative_selector_search_direction(&element, prev_sibling);
if inherited.is_empty() {
return;
}
// Same comment as insertion applies.
match (prev_sibling, next_sibling) {
(Some(prev_sibling), Some(next_sibling)) => {
invalidate_relative_selector_prev_sibling_side_effect(
prev_sibling,
quirks_mode,
SiblingTraversalMap::default(),
&data.stylist
);
invalidate_relative_selector_next_sibling_side_effect(
next_sibling,
quirks_mode,
SiblingTraversalMap::default(),
&data.stylist,
);
},
_ => (),
};
let invalidator = RelativeSelectorInvalidator {
element,
quirks_mode,
sibling_traversal_map: SiblingTraversalMap::new(element, prev_sibling, next_sibling),
invalidated: relative_selector_invalidated_at,
_marker: std::marker::PhantomData,
};
invalidator.invalidate_relative_selectors_for_dom_mutation(
true,
&data.stylist,
inherited,
|_| true,
);
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_HasStateDependency(
raw_data: &PerDocumentStyleData,
@@ -7155,6 +7376,7 @@ fn process_relative_selector_invalidations(
element: *element,
quirks_mode,
invalidated: relative_selector_invalidated_at,
sibling_traversal_map: SiblingTraversalMap::default(),
_marker: std::marker::PhantomData,
};

View File

@@ -1,24 +0,0 @@
[attribute-or-elemental-selectors-in-has.html]
[add descendant to #div_subject: div#div_subject.color]
expected: FAIL
[add "div > descendant" to #div_subject: div#div_subject.color]
expected: FAIL
[add div.child to #div_subject: div#div_subject.color]
expected: FAIL
[add "div > div.descendant" to #div_subject: div#div_subject.color]
expected: FAIL
[add div#div_descendant to #div_subject: div#div_subject.color]
expected: FAIL
[add "div#div_descendant" to #div_subject: div#div_subject.color]
expected: FAIL
[add div[attrname\] to #div_subject: div#div_subject.color]
expected: FAIL
[add "div > div[attrname\]" to #div_subject: div#div_subject.color]
expected: FAIL

View File

@@ -1,46 +1,4 @@
[child-indexed-pseudo-classes-in-has.html]
[Prepend #div1.green: #only_child]
expected: FAIL
[Prepend #div1.green: #first_child]
expected: FAIL
[Prepend #div1.green: #last_child]
expected: FAIL
[Prepend #div1.green: #nth_child_3n_1]
expected: FAIL
[Prepend #div2.yellow: #first_child]
expected: FAIL
[Prepend #div2.yellow: #last_child]
expected: FAIL
[Prepend #div2.yellow: #nth_child_3n_1]
expected: FAIL
[Prepend #div2.yellow: #nth_child_3n_2]
expected: FAIL
[Prepend #div3.orange: #first_child]
expected: FAIL
[Prepend #div3.orange: #last_child]
expected: FAIL
[Prepend #div3.orange: #nth_child_3n_1]
expected: FAIL
[Prepend #div3.orange: #nth_child_3n_2]
expected: FAIL
[Prepend #div3.orange: #nth_child_3n]
expected: FAIL
[Prepend #div4: #last_child]
expected: FAIL
[Prepend #div4: #nth_child_3n_1]
expected: FAIL
@@ -50,9 +8,6 @@
[Prepend #div4: #nth_child_3n]
expected: FAIL
[Prepend #div5: #last_child]
expected: FAIL
[Prepend #div5: #nth_child_3n_1]
expected: FAIL
@@ -62,20 +17,8 @@
[Prepend #div5: #nth_child_3n]
expected: FAIL
[Remove #div1: #last_child]
[Prepend #div4: #first_child]
expected: FAIL
[Remove #div1: #nth_child_3n_1]
expected: FAIL
[Remove #div1: #nth_child_3n]
expected: FAIL
[Remove #div2: #last_child]
expected: FAIL
[Remove #div2: #nth_child_3n]
expected: FAIL
[Remove #div4: #only_child]
[Prepend #div5: #first_child]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[dir-pseudo-class-in-has.html]
expected: FAIL

View File

@@ -1,8 +0,0 @@
[empty-pseudo-in-has.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Insert div#child to #subject]
expected: FAIL
[Insert div to div.#child]
expected: FAIL

View File

@@ -1,3 +0,0 @@
[fullscreen-pseudo-class-in-has.html]
[:fullscreen pseudo-class invalidation with requestFullscreen + remove]
expected: FAIL

View File

@@ -1,264 +0,0 @@
[has-in-adjacent-position.html]
[insert element div.test before previous_sibling_child]
expected: FAIL
[insert element div.test before previous_sibling_descendant]
expected: FAIL
[insert element div.test before next_sibling]
expected: FAIL
[insert element div.test before next_sibling_child]
expected: FAIL
[insert element div.test before next_sibling_descendant]
expected: FAIL
[insert element div.test after previous_sibling_child]
expected: FAIL
[insert element div.test after previous_sibling_descendant]
expected: FAIL
[insert element div.test after subject]
expected: FAIL
[insert element div.test after next_sibling]
expected: FAIL
[insert element div.test after next_sibling_child]
expected: FAIL
[insert element div.test after next_sibling_descendant]
expected: FAIL
[insert tree div>div.test before previous_sibling_child]
expected: FAIL
[insert tree div>div.test before previous_sibling_descendant]
expected: FAIL
[insert tree div>div.test before next_sibling]
expected: FAIL
[insert tree div>div.test before next_sibling_child]
expected: FAIL
[insert tree div>div.test before next_sibling_descendant]
expected: FAIL
[insert tree div>div.test after previous_sibling_child]
expected: FAIL
[insert tree div>div.test after previous_sibling_descendant]
expected: FAIL
[insert tree div>div.test after subject]
expected: FAIL
[insert tree div>div.test after next_sibling]
expected: FAIL
[insert tree div>div.test after next_sibling_child]
expected: FAIL
[insert tree div>div.test after next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] before previous_sibling_child]
expected: FAIL
[insert element div[test_attr\] before previous_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] before next_sibling]
expected: FAIL
[insert element div[test_attr\] before next_sibling_child]
expected: FAIL
[insert element div[test_attr\] before next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] after previous_sibling_child]
expected: FAIL
[insert element div[test_attr\] after previous_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] after subject]
expected: FAIL
[insert element div[test_attr\] after next_sibling]
expected: FAIL
[insert element div[test_attr\] after next_sibling_child]
expected: FAIL
[insert element div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before previous_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before previous_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] before previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before previous_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] before previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after previous_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after previous_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] after previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after previous_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] after previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject]
expected: FAIL
[insert element div>div[test_attr\] after subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before previous_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again before previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted before previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after previous_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again after previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted after previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after subject]
expected: FAIL
[add the class 'test' to the element inserted again after subject]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_descendant]
expected: FAIL

View File

@@ -1,362 +0,0 @@
[has-in-ancestor-position.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[insert element div.test before subject_parent]
expected: FAIL
[insert element div.test before subject]
expected: FAIL
[insert element div.test before subject_child]
expected: FAIL
[insert element div.test before subject_descendant]
expected: FAIL
[insert element div.test before next_sibling]
expected: FAIL
[insert element div.test before next_sibling_child]
expected: FAIL
[insert element div.test before next_sibling_descendant]
expected: FAIL
[insert element div.test after subject_ancestor]
expected: FAIL
[insert element div.test after subject_parent]
expected: FAIL
[insert element div.test after subject]
expected: FAIL
[insert element div.test after subject_child]
expected: FAIL
[insert element div.test after subject_descendant]
expected: FAIL
[insert element div.test after next_sibling]
expected: FAIL
[insert element div.test after next_sibling_child]
expected: FAIL
[insert element div.test after next_sibling_descendant]
expected: FAIL
[insert tree div>div.test before subject]
expected: FAIL
[insert tree div>div.test before subject_child]
expected: FAIL
[insert tree div>div.test before subject_descendant]
expected: FAIL
[insert tree div>div.test before next_sibling]
expected: FAIL
[insert tree div>div.test before next_sibling_child]
expected: FAIL
[insert tree div>div.test before next_sibling_descendant]
expected: FAIL
[insert tree div>div.test after subject_ancestor]
expected: FAIL
[insert tree div>div.test after subject_parent]
expected: FAIL
[insert tree div>div.test after subject]
expected: FAIL
[insert tree div>div.test after subject_child]
expected: FAIL
[insert tree div>div.test after subject_descendant]
expected: FAIL
[insert tree div>div.test after next_sibling]
expected: FAIL
[insert tree div>div.test after next_sibling_child]
expected: FAIL
[insert tree div>div.test after next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] before subject_parent]
expected: FAIL
[insert element div[test_attr\] before subject]
expected: FAIL
[insert element div[test_attr\] before subject_child]
expected: FAIL
[insert element div[test_attr\] before subject_descendant]
expected: FAIL
[insert element div[test_attr\] before next_sibling]
expected: FAIL
[insert element div[test_attr\] before next_sibling_child]
expected: FAIL
[insert element div[test_attr\] before next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] after subject_ancestor]
expected: FAIL
[insert element div[test_attr\] after subject_parent]
expected: FAIL
[insert element div[test_attr\] after subject]
expected: FAIL
[insert element div[test_attr\] after subject_child]
expected: FAIL
[insert element div[test_attr\] after subject_descendant]
expected: FAIL
[insert element div[test_attr\] after next_sibling]
expected: FAIL
[insert element div[test_attr\] after next_sibling_child]
expected: FAIL
[insert element div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject]
expected: FAIL
[insert element div>div[test_attr\] before subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject_child]
expected: FAIL
[insert element div>div[test_attr\] before subject_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject_descendant]
expected: FAIL
[insert element div>div[test_attr\] before subject_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_ancestor]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_ancestor]
expected: FAIL
[insert element div>div[test_attr\] after subject_ancestor]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_parent]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_parent]
expected: FAIL
[insert element div>div[test_attr\] after subject_parent]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject]
expected: FAIL
[insert element div>div[test_attr\] after subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_child]
expected: FAIL
[insert element div>div[test_attr\] after subject_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_descendant]
expected: FAIL
[insert element div>div[test_attr\] after subject_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before subject_parent]
expected: FAIL
[add the class 'test' to the element inserted again before subject_parent]
expected: FAIL
[add the class 'test' again to the element inserted before subject]
expected: FAIL
[add the class 'test' to the element inserted again before subject]
expected: FAIL
[add the class 'test' again to the element inserted before subject_child]
expected: FAIL
[add the class 'test' to the element inserted again before subject_child]
expected: FAIL
[add the class 'test' again to the element inserted before subject_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before subject_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after subject_ancestor]
expected: FAIL
[add the class 'test' to the element inserted again after subject_ancestor]
expected: FAIL
[add the class 'test' again to the element inserted after subject_parent]
expected: FAIL
[add the class 'test' to the element inserted again after subject_parent]
expected: FAIL
[add the class 'test' again to the element inserted after subject]
expected: FAIL
[add the class 'test' to the element inserted again after subject]
expected: FAIL
[add the class 'test' again to the element inserted after subject_child]
expected: FAIL
[add the class 'test' to the element inserted again after subject_child]
expected: FAIL
[add the class 'test' again to the element inserted after subject_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after subject_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_descendant]
expected: FAIL
[insert tree div>div.test before subject_parent]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject_parent]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject_parent]
expected: FAIL
[insert element div>div[test_attr\] before subject_parent]
expected: FAIL

View File

@@ -1,170 +0,0 @@
[has-in-parent-position.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[insert element div.test before subject]
expected: FAIL
[insert element div.test before subject_child]
expected: FAIL
[insert element div.test before subject_descendant]
expected: FAIL
[insert element div.test after subject_parent]
expected: FAIL
[insert element div.test after subject]
expected: FAIL
[insert element div.test after subject_child]
expected: FAIL
[insert element div.test after subject_descendant]
expected: FAIL
[insert tree div>div.test before subject]
expected: FAIL
[insert tree div>div.test before subject_child]
expected: FAIL
[insert tree div>div.test before subject_descendant]
expected: FAIL
[insert tree div>div.test after subject_parent]
expected: FAIL
[insert tree div>div.test after subject]
expected: FAIL
[insert tree div>div.test after subject_child]
expected: FAIL
[insert tree div>div.test after subject_descendant]
expected: FAIL
[insert element div[test_attr\] before subject]
expected: FAIL
[insert element div[test_attr\] before subject_child]
expected: FAIL
[insert element div[test_attr\] before subject_descendant]
expected: FAIL
[insert element div[test_attr\] after subject_parent]
expected: FAIL
[insert element div[test_attr\] after subject]
expected: FAIL
[insert element div[test_attr\] after subject_child]
expected: FAIL
[insert element div[test_attr\] after subject_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject]
expected: FAIL
[insert element div>div[test_attr\] before subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject_child]
expected: FAIL
[insert element div>div[test_attr\] before subject_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before subject_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before subject_descendant]
expected: FAIL
[insert element div>div[test_attr\] before subject_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_parent]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_parent]
expected: FAIL
[insert element div>div[test_attr\] after subject_parent]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject]
expected: FAIL
[insert element div>div[test_attr\] after subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_child]
expected: FAIL
[insert element div>div[test_attr\] after subject_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject_descendant]
expected: FAIL
[insert element div>div[test_attr\] after subject_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before subject]
expected: FAIL
[add the class 'test' to the element inserted again before subject]
expected: FAIL
[add the class 'test' again to the element inserted before subject_child]
expected: FAIL
[add the class 'test' to the element inserted again before subject_child]
expected: FAIL
[add the class 'test' again to the element inserted before subject_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before subject_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after subject_parent]
expected: FAIL
[add the class 'test' to the element inserted again after subject_parent]
expected: FAIL
[add the class 'test' again to the element inserted after subject]
expected: FAIL
[add the class 'test' to the element inserted again after subject]
expected: FAIL
[add the class 'test' again to the element inserted after subject_child]
expected: FAIL
[add the class 'test' to the element inserted again after subject_child]
expected: FAIL
[add the class 'test' again to the element inserted after subject_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after subject_descendant]
expected: FAIL

View File

@@ -1,264 +0,0 @@
[has-in-sibling-position.html]
[insert element div.test before previous_sibling_child]
expected: FAIL
[insert element div.test before previous_sibling_descendant]
expected: FAIL
[insert element div.test before next_sibling]
expected: FAIL
[insert element div.test before next_sibling_child]
expected: FAIL
[insert element div.test before next_sibling_descendant]
expected: FAIL
[insert element div.test after previous_sibling_child]
expected: FAIL
[insert element div.test after previous_sibling_descendant]
expected: FAIL
[insert element div.test after subject]
expected: FAIL
[insert element div.test after next_sibling]
expected: FAIL
[insert element div.test after next_sibling_child]
expected: FAIL
[insert element div.test after next_sibling_descendant]
expected: FAIL
[insert tree div>div.test before previous_sibling_child]
expected: FAIL
[insert tree div>div.test before previous_sibling_descendant]
expected: FAIL
[insert tree div>div.test before next_sibling]
expected: FAIL
[insert tree div>div.test before next_sibling_child]
expected: FAIL
[insert tree div>div.test before next_sibling_descendant]
expected: FAIL
[insert tree div>div.test after previous_sibling_child]
expected: FAIL
[insert tree div>div.test after previous_sibling_descendant]
expected: FAIL
[insert tree div>div.test after subject]
expected: FAIL
[insert tree div>div.test after next_sibling]
expected: FAIL
[insert tree div>div.test after next_sibling_child]
expected: FAIL
[insert tree div>div.test after next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] before previous_sibling_child]
expected: FAIL
[insert element div[test_attr\] before previous_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] before next_sibling]
expected: FAIL
[insert element div[test_attr\] before next_sibling_child]
expected: FAIL
[insert element div[test_attr\] before next_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] after previous_sibling_child]
expected: FAIL
[insert element div[test_attr\] after previous_sibling_descendant]
expected: FAIL
[insert element div[test_attr\] after subject]
expected: FAIL
[insert element div[test_attr\] after next_sibling]
expected: FAIL
[insert element div[test_attr\] after next_sibling_child]
expected: FAIL
[insert element div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before previous_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before previous_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] before previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before previous_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] before previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again before next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after previous_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after previous_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] after previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after previous_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] after previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after subject]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after subject]
expected: FAIL
[insert element div>div[test_attr\] after subject]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_child]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element in the tree inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element in the tree inserted again after next_sibling_descendant]
expected: FAIL
[insert element div>div[test_attr\] after next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before previous_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again before previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted before previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted before next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again before next_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after previous_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again after previous_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted after previous_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after previous_sibling_descendant]
expected: FAIL
[add the class 'test' again to the element inserted after subject]
expected: FAIL
[add the class 'test' to the element inserted again after subject]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_child]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_child]
expected: FAIL
[add the class 'test' again to the element inserted after next_sibling_descendant]
expected: FAIL
[add the class 'test' to the element inserted again after next_sibling_descendant]
expected: FAIL

View File

@@ -1,5 +0,0 @@
[has-invalidation-after-removing-non-first-element.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[remove descendant: div#subject.color]
expected: FAIL

View File

@@ -1,6 +0,0 @@
[has-invalidation-for-wiping-an-element.html]
[color after inserting text and div > .descendant: div#subject.color]
expected: FAIL
[color after inserting text and #child > .descendant: div#subject.color]
expected: FAIL

View File

@@ -1,86 +0,0 @@
[has-sibling.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[insert element div.test before first_sibling]
expected: FAIL
[insert element div.test before second_sibling]
expected: FAIL
[insert element div.test before third_sibling]
expected: FAIL
[insert element div.test before first_sibling_child]
expected: FAIL
[insert element div.test before first_sibling_descendant]
expected: FAIL
[insert element div.test before third_sibling_child]
expected: FAIL
[insert element div.test before third_sibling_descendant]
expected: FAIL
[insert element div.test after first_sibling]
expected: FAIL
[insert element div.test after second_sibling]
expected: FAIL
[insert element div.test after third_sibling]
expected: FAIL
[insert element div.test after first_sibling_child]
expected: FAIL
[insert element div.test after first_sibling_descendant]
expected: FAIL
[insert element div.test after third_sibling_child]
expected: FAIL
[insert element div.test after third_sibling_descendant]
expected: FAIL
[insert tree div>div.test before first_sibling]
expected: FAIL
[insert tree div>div.test before second_sibling]
expected: FAIL
[insert tree div>div.test before third_sibling]
expected: FAIL
[insert tree div>div.test before first_sibling_child]
expected: FAIL
[insert tree div>div.test before first_sibling_descendant]
expected: FAIL
[insert tree div>div.test before third_sibling_child]
expected: FAIL
[insert tree div>div.test before third_sibling_descendant]
expected: FAIL
[insert tree div>div.test after first_sibling]
expected: FAIL
[insert tree div>div.test after second_sibling]
expected: FAIL
[insert tree div>div.test after third_sibling]
expected: FAIL
[insert tree div>div.test after first_sibling_child]
expected: FAIL
[insert tree div>div.test after first_sibling_descendant]
expected: FAIL
[insert tree div>div.test after third_sibling_child]
expected: FAIL
[insert tree div>div.test after third_sibling_descendant]
expected: FAIL

View File

@@ -1,26 +0,0 @@
[has-with-not.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[insert element div before subject_child]
expected: FAIL
[insert element div before subject_descendant]
expected: FAIL
[insert element div after subject_child]
expected: FAIL
[insert element div after subject_descendant]
expected: FAIL
[insert tree div>div before subject_child]
expected: FAIL
[insert tree div>div before subject_descendant]
expected: FAIL
[insert tree div.test after subject_child]
expected: FAIL
[insert tree div.test after subject_descendant]
expected: FAIL

View File

@@ -1,6 +0,0 @@
[has-with-nth-child.html]
[:nth-child() no longer matching after removal]
expected: FAIL
[:nth-child() in non-subject no longer matching after removal]
expected: FAIL

View File

@@ -1,29 +0,0 @@
[input-pseudo-classes-in-has.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[:checked & :indeterminate invalidation on <input>]
expected: FAIL
[:indeterminate invalidation on <progress>]
expected: FAIL
[:disabled invalidation]
expected: FAIL
[:read-only invalidation]
expected: FAIL
[:valid invalidation]
expected: FAIL
[:default invalidation with input[type=radio\]]
expected: FAIL
[:required invalidation]
expected: FAIL
[:out-of-range invalidation]
expected: FAIL
[:placeholder-shown invalidation]
expected: FAIL

View File

@@ -1,62 +1,17 @@
[is-pseudo-containing-sibling-relationship-in-has.html]
[sibling selector enclosed by :is() no longer matching after removal (1)]
expected: FAIL
[sibling selector enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[sibling selector enclosed by :is() no longer matching after removal (3)]
expected: FAIL
[sibling selector enclosed by :is() no longer matching after removal (4)]
expected: FAIL
[sibling selector in non-subject enclosed by :is() no longer matching after removal (1)]
expected: FAIL
[sibling selector in non-subject enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[sibling selector in non-subject enclosed by :is() no longer matching after removal (3)]
expected: FAIL
[sibling selector in non-subject enclosed by :is() no longer matching after removal (4)]
expected: FAIL
[sibling selector in non-subject enclosed by :is() no longer matching after removal (5)]
expected:
if not asan and tsan: [FAIL, PASS]
if asan: [FAIL, PASS]
FAIL
[:nth-child() enclosed by :is() no longer matching after removal (1)]
expected:
if not asan and tsan: [FAIL, PASS]
if asan: [FAIL, PASS]
FAIL
[:nth-child() enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[:nth-last-child() enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[:nth-child() in non-subject enclosed by :is() no longer matching after removal (1)]
expected: FAIL
[:nth-child() in non-subject enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[sibling selector in parent selector enclosed by :is() no longer matching after removal]
expected: FAIL
[sibling selector in parent selector non-subject position enclosed by :is() no longer matching after removal (1)]
expected:
if tsan: [FAIL, PASS]
FAIL
[sibling selector in parent selector non-subject position enclosed by :is() no longer matching after removal (2)]
expected: FAIL
[Initially red]
expected:

View File

@@ -1,5 +0,0 @@
[link-pseudo-in-has.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[CSS Selectors Invalidation: :link, :visited :any-link, pseudo-class in :has() argument]
expected: FAIL

View File

@@ -1,11 +0,0 @@
[modal-pseudo-class-in-has.html]
expected:
if (os == "android") and fission: [ERROR, TIMEOUT, OK]
[:modal pseudo-class invalidation with showModal + remove]
expected: FAIL
[:modal pseudo-class invalidation with requestFullscreen + exitFullscreen]
expected: FAIL
[:modal pseudo-class invalidation with requestFullscreen + remove]
expected: FAIL

View File

@@ -1,3 +0,0 @@
[not-pseudo-containing-sibling-relationship-in-has.html]
[:nth-child() enclosed by :not() matching after insertion]
expected: FAIL

View File

@@ -1,87 +1,6 @@
[typed-child-indexed-pseudo-classes-in-has.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Prepend #div1.green: #only_of_type]
expected: FAIL
[Prepend #div1.green: #first_of_type]
expected: FAIL
[Prepend #div1.green: #last_of_type]
expected: FAIL
[Prepend #div1.green: #nth_of_type_3n_1]
expected: FAIL
[Prepend span (2): #only_of_type]
expected: FAIL
[Prepend span (2): #first_of_type]
expected: FAIL
[Prepend span (2): #last_of_type]
expected: FAIL
[Prepend span (2): #nth_of_type_3n_1]
expected: FAIL
[Prepend #div2.yellow: #first_of_type]
expected: FAIL
[Prepend #div2.yellow: #last_of_type]
expected: FAIL
[Prepend #div2.yellow: #nth_of_type_3n_1]
expected: FAIL
[Prepend #div2.yellow: #nth_of_type_3n_2]
expected: FAIL
[Prepend span (3): #first_of_type]
expected: FAIL
[Prepend span (3): #last_of_type]
expected: FAIL
[Prepend span (3): #nth_of_type_3n_1]
expected: FAIL
[Prepend span (3): #nth_of_type_3n_2]
expected: FAIL
[Prepend #div3.orange: #first_of_type]
expected: FAIL
[Prepend #div3.orange: #last_of_type]
expected: FAIL
[Prepend #div3.orange: #nth_of_type_3n_1]
expected: FAIL
[Prepend #div3.orange: #nth_of_type_3n_2]
expected: FAIL
[Prepend #div3.orange: #nth_of_type_3n]
expected: FAIL
[Prepend span (4): #first_of_type]
expected: FAIL
[Prepend span (4): #last_of_type]
expected: FAIL
[Prepend span (4): #nth_of_type_3n_1]
expected: FAIL
[Prepend span (4): #nth_of_type_3n_2]
expected: FAIL
[Prepend span (4): #nth_of_type_3n]
expected: FAIL
[Prepend #div4: #last_of_type]
expected: FAIL
[Prepend #div4: #nth_of_type_3n_1]
expected: FAIL
@@ -91,9 +10,6 @@
[Prepend #div4: #nth_of_type_3n]
expected: FAIL
[Prepend span (5): #last_of_type]
expected: FAIL
[Prepend span (5): #nth_of_type_3n_1]
expected: FAIL
@@ -103,9 +19,6 @@
[Prepend span (5): #nth_of_type_3n]
expected: FAIL
[Prepend #div5: #last_of_type]
expected: FAIL
[Prepend #div5: #nth_of_type_3n_1]
expected: FAIL
@@ -115,9 +28,6 @@
[Prepend #div5: #nth_of_type_3n]
expected: FAIL
[Prepend span (6): #last_of_type]
expected: FAIL
[Prepend span (6): #nth_of_type_3n_1]
expected: FAIL
@@ -127,20 +37,14 @@
[Prepend span (6): #nth_of_type_3n]
expected: FAIL
[Remove #div1: #last_of_type]
[Prepend #div4: #first_of_type]
expected: FAIL
[Remove #div1: #nth_of_type_3n_1]
[Prepend span (5): #first_of_type]
expected: FAIL
[Remove #div1: #nth_of_type_3n]
[Prepend #div5: #first_of_type]
expected: FAIL
[Remove #div2: #last_of_type]
expected: FAIL
[Remove #div2: #nth_of_type_3n]
expected: FAIL
[Remove #div4: #only_of_type]
[Prepend span (6): #first_of_type]
expected: FAIL

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Selector Invalidation: Invalidate :has() as result of insertion/removal</title>
<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<style>
div, main { color: grey }
#subject:has(+ #next_sibling) { color: red; }
#prev_sibling:has(+ #subject + #next_sibling) { color: green; }
</style>
<main id=main>
<div id=prev_sibling></div>
<div id=subject></div>
<div id=blocks_match></div>
<div id=next_sibling></div>
</main>
<script>
const grey = 'rgb(128, 128, 128)';
const red = 'rgb(255, 0, 0)';
const green = 'rgb(0, 128, 0)';
function testColors(test_name, subject_color, prev_sibling_color) {
test(function() {
assert_equals(getComputedStyle(subject).color, subject_color);
assert_equals(getComputedStyle(prev_sibling).color, prev_sibling_color);
}, test_name);
}
testColors('Initial colors', grey, grey);
const d = blocks_match;
d.remove();
testColors('Matches after #blocks_match removed', red, green);
subject.after(d);
testColors('Does not match after #blocks_match added', grey, grey);
</script>