# Implemented Pseudo Element* \*Definition given by this document. ## Definition The *implemented pseudo element* within Firefox's codebase could be referred as a **private** concrete element that would match the selector of a pseudo element, this would include the internal pseudo elements for the implementation of defined HTML element. The private here means that the element would reside inside an UA widget. One of the main trait of the pseudo element is that it would have a specific definition for the [pseudo originating element](https://www.w3.org/TR/selectors-4/#originating-element). In Servo this would be the shadow host and for Firefox it would be root of the anonymous tree. The pseudo originating element would act as similar how to a parent would do in a style computation. > [!Note] > This differ from our definition of `pseudo` inside the `ServoLayoutNode`, where it does not have a concrete element behind it. And then, since the "private" status of the element, it shouldn't be exposed to JS as well. ## Resolving an Implemented Pseudo Element One of the main result of style resolving `resolve_style_with_default_parents` for an element is the `ResolvedElementStyles`, which contains the style for an element. ```rs /// A set of style returned from the resolver machinery. pub struct ResolvedElementStyles { /// Primary style. pub primary: PrimaryStyle, /// Pseudo styles. pub pseudos: EagerPseudoStyles, } /// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements. /// /// We use an Arc so that sharing these styles via the style sharing cache does /// not require duplicate allocations. We leverage the copy-on-write semantics of /// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations) /// in servo_arc. #[derive(Clone, Debug, Default)] pub struct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>); ``` Primary style is the style for the element, while pseudo styles is the style for each pseudo element this element could relate to. Note that an element might not have a pseudo element style, and therefore the pseudo style would be `None`. The style computation of this kind of pseudo element is kinda different to how a lazy pseudo element was computed. Specifically, the *implemented pseudo element* itself would **have a primary style** that is the result of resolving and cascading included with its pseudo selector. But it **wouldn't have any pseudo styles**. ## General Behavior Behavior of style resolving of an element is managed by the implementations of traits `selector::Element` and `style::dom::TElement`. **TBA Details for those two** This section will explain the implementation changes following the behavior of implemented pseudo element. Codes shown here is after the changes. ### style::dom::TElement::inheritance_parent Determines which parent does this element style could inherit from. Servo ```rs fn inheritance_parent(&self) -> Option<Self> { if self.is_pseudo_element() { // Inheritance parent of an implemented pseudo element should be the // pseudo element originating element, except for element backed pseudo. // But Servo are yet to implement that type of pseudo elements. // // FIXME: handle the cases of element backed pseudo return self.pseudo_element_originating_element(); } // FIXME: In default the inheritance parent would be the Self::parent_element // but probably we should use the flattened tree parent. self.traversal_parent() } ``` Gecko ```rs fn inheritance_parent(&self) -> Option<Self> { if let Some(pseudo) = self.implemented_pseudo_element() { if !pseudo.is_element_backed() { return self.pseudo_element_originating_element(); } } self.as_node() .flattened_tree_parent() .and_then(|n| n.as_element()) } ``` ### style::dom::TElement::matches_user_and_content_rules Whether this should match rules from docs, user origin, rules from containing shadow tree, and slotted rules. Servo ```rs /// We would like to match rules from the same tree in all cases and optimize computation. /// UA Widget is an exception since we could have a pseudo element selector inside it. #[inline] fn matches_user_and_content_rules(&self) -> bool { !self.as_node().node.is_in_ua_widget() } ``` Gecko ```rs /// We want to match rules from the same tree in all cases, except for native anonymous content /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or /// pseudo-elements). #[inline] fn matches_user_and_content_rules(&self) -> bool { !self.in_native_anonymous_subtree() || self.in_ua_widget() } ``` > [!Note] > Element that does not matches rules from the same trees is omitting several optimization such as the style sharing. > **NOTE: Could add more details.** ### style::dom::TElement::implemented_pseudo_element To get which pseudo element this element should match in style cascading. Servo ```rs /// Returns the pseudo-element implemented by this element, if any. In other words, /// the element will match the specified pseudo element throughout the style computation. #[inline] fn implemented_pseudo_element(&self) -> Option<PseudoElement> { if self.matches_user_and_content_rules() { return None; } self.as_node().node.pseudo_element() } ``` Gecko ```rs #[inline] fn implemented_pseudo_element(&self) -> Option<PseudoElement> { if !self.in_native_anonymous_subtree() { return None; } if !self.has_properties() { return None; } let name = unsafe { bindings::Gecko_GetImplementedPseudoIdentifier(self.0) }; PseudoElement::from_pseudo_type( unsafe { bindings::Gecko_GetImplementedPseudoType(self.0) }, if name.is_null() { None } else { Some(AtomIdent::new(unsafe { Atom::from_raw(name) })) }, ) } ``` > [!Note] > > The `Gecko_GetImplementedPseudoIdentifier` is used to handle named view transition group. > > While `PseudoElement::from_pseudo_type` will get the `PseudoElement` (stylo struct) from the type, with the examples as follow. > ```rs > PseudoStyleType::placeholder => { > debug_assert!(functional_pseudo_parameter.is_none()); > Some(PseudoElement::Placeholder) > }, > PseudoStyleType::mozColorSwatch => { > debug_assert!(functional_pseudo_parameter.is_none()); > Some(PseudoElement::MozColorSwatch) > }, > PseudoStyleType::mozTextControlEditingRoot => { > debug_assert!(functional_pseudo_parameter.is_none()); > Some(PseudoElement::MozTextControlEditingRoot) > }, > ``` ### ::selectors::Element::pseudo_element_originating_element For the inheritance and to handle the relation in the pseudo element selector matching (e.g. `input::-servo-inner-container`). Servo ```rs #[inline] fn pseudo_element_originating_element(&self) -> Option<Self> { debug_assert!(self.is_pseudo_element()); debug_assert!(!self.matches_user_and_content_rules()); if self.element.upcast::<Node>().is_in_ua_widget() { self.containing_shadow_host() } else { self.parent_element() } } ``` Gecko ```rs #[inline] fn pseudo_element_originating_element(&self) -> Option<Self> { debug_assert!(self.is_pseudo_element()); debug_assert!(self.in_native_anonymous_subtree()); if self.in_ua_widget() { return self.containing_shadow_host(); } let mut current = *self; loop { let anon_root = current.is_root_of_native_anonymous_subtree(); current = current.traversal_parent()?; if anon_root { return Some(current); } } } ``` ### ::selectors::Element::match_pseudo_element To handle the selector matching (e.g. `::-servo-inner-container`). Servo ```rs fn match_pseudo_element( &self, pseudo: &PseudoElement, _context: &mut MatchingContext<Self::Impl>, ) -> bool { self.implemented_pseudo_element() == Some(*pseudo) } ``` Gecko ```rs fn match_pseudo_element( &self, pseudo_selector: &PseudoElement, _context: &mut MatchingContext<Self::Impl>, ) -> bool { // TODO(emilio): I believe we could assert we are a pseudo-element and // match the proper pseudo-element, given how we rulehash the stuff // based on the pseudo. let pseudo = match self.implemented_pseudo_element() { Some(pseudo) => pseudo, None => return false, }; pseudo.matches(pseudo_selector) } ``` > [!Note] > Maybe need to check more details from `::selectors::matching:matches_simple_selector`. > > ```rs > /// Determines whether the given element matches the given single selector. > fn matches_simple_selector<E>( > selector: &Component<E::Impl>, > element: &E, > context: &mut LocalMatchingContext<E::Impl>, > ) -> KleeneValue > where > E: Element, > { > ``` ### ::selectors::Element::parent_node_is_shadow_root ### ::selectors::Element::containing_shadow_host