mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
script: Use an implemented pseudo-element to fortype=color
::color-swatch
(#37427)
Implement internal pseudo element, which would be resolved as a "Implemented Pseudo Element" within style computation. This is an concrete element that would has a primary style after the style computation, but could match and style resolved like an pseudo element. Therefore, it would have a different behavior compared to how does `pseudo`s that `ServoLayoutNode` had. Where they would not have a concrete element behind it. Note that, due to the nature of these pseudo elements residing inside a UA widget, these pseudo elements would therefore not be accessible in JavaScript by default. This kind of element is required in order to implement the [form control pseudo element](https://drafts.csswg.org/css-forms-1/#pseudo-elements) like `::placeholder`, `::color-swatch`, `::field-text`, etc. See [this docs](https://hackmd.io/@ChaKweTiau/BJ3zRdLQlg) for more details of the implementation. Then, the implemented pseudo element is utilized to implement style matching for input `type=text`. Servo's side of: https://github.com/servo/stylo/pull/212 Testing: No WPT regression. --------- Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
d2ccf419c3
commit
378c4648e4
12 changed files with 201 additions and 163 deletions
|
@ -687,6 +687,43 @@ impl Element {
|
|||
Ok(shadow_root)
|
||||
}
|
||||
|
||||
/// Attach a UA widget shadow root with its default parameters.
|
||||
/// Additionally mark ShadowRoot to use styling configuration for a UA widget.
|
||||
///
|
||||
/// The general trait of these elements is that it would hide the implementation.
|
||||
/// Thus, we would make it inaccessible (i.e., closed mode, not cloneable, and
|
||||
/// not serializable).
|
||||
///
|
||||
/// With UA shadow root element being assumed as one element, any focus should
|
||||
/// be delegated to its host.
|
||||
///
|
||||
// TODO: Ideally, all of the UA shadow root should use UA widget styling, but
|
||||
// some of the UA widget implemented prior to the implementation of Gecko's
|
||||
// UA widget matching might need some tweaking.
|
||||
// FIXME: We are yet to implement more complex focusing with that is necessary
|
||||
// for delegate focus, and we are using workarounds for that right now.
|
||||
pub(crate) fn attach_ua_shadow_root(
|
||||
&self,
|
||||
use_ua_widget_styling: bool,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<ShadowRoot> {
|
||||
let root = self
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed");
|
||||
|
||||
root.upcast::<Node>()
|
||||
.set_in_ua_widget(use_ua_widget_styling);
|
||||
root
|
||||
}
|
||||
|
||||
pub(crate) fn detach_shadow(&self, can_gc: CanGc) {
|
||||
let Some(ref shadow_root) = self.shadow_root() else {
|
||||
unreachable!("Trying to detach a non-attached shadow root");
|
||||
|
|
|
@ -13,9 +13,6 @@ use crate::dom::bindings::cell::DomRefCell;
|
|||
use crate::dom::bindings::codegen::Bindings::HTMLDetailsElementBinding::HTMLDetailsElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::UnionTypes::ElementOrText;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
|
@ -26,7 +23,6 @@ use crate::dom::eventtarget::EventTarget;
|
|||
use crate::dom::htmlelement::HTMLElement;
|
||||
use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeDamage, NodeTraits};
|
||||
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
@ -103,18 +99,11 @@ impl HTMLDetailsElement {
|
|||
|
||||
fn create_shadow_tree(&self, can_gc: CanGc) {
|
||||
let document = self.owner_document();
|
||||
// TODO(stevennovaryo): Reimplement details styling so that it would not
|
||||
// mess the cascading and require some reparsing.
|
||||
let root = self
|
||||
.upcast::<Element>()
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed");
|
||||
.attach_ua_shadow_root(false, can_gc);
|
||||
|
||||
let summary = HTMLSlotElement::new(local_name!("slot"), None, &document, None, can_gc);
|
||||
root.upcast::<Node>()
|
||||
|
|
|
@ -29,10 +29,8 @@ use js::rust::{HandleObject, MutableHandleObject};
|
|||
use net_traits::blob_url_store::get_blob_origin;
|
||||
use net_traits::filemanager_thread::FileManagerThreadMsg;
|
||||
use net_traits::{CoreResourceMsg, IpcSend};
|
||||
use script_bindings::codegen::GenericBindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use style::attr::AttrValue;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::str::{split_commas, str_join};
|
||||
use stylo_atoms::Atom;
|
||||
use stylo_dom::ElementState;
|
||||
|
@ -59,7 +57,7 @@ use crate::dom::bindings::str::{DOMString, FromInputValueString, ToInputValueStr
|
|||
use crate::dom::clipboardevent::ClipboardEvent;
|
||||
use crate::dom::compositionevent::CompositionEvent;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::{AttributeMutation, Element, ElementCreator, LayoutElementHelpers};
|
||||
use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::file::File;
|
||||
|
@ -73,14 +71,13 @@ use crate::dom::htmlformelement::{
|
|||
FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, ResetFrom,
|
||||
SubmittedFrom,
|
||||
};
|
||||
use crate::dom::htmlstyleelement::HTMLStyleElement;
|
||||
use crate::dom::keyboardevent::KeyboardEvent;
|
||||
use crate::dom::mouseevent::MouseEvent;
|
||||
use crate::dom::node::{
|
||||
BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext,
|
||||
};
|
||||
use crate::dom::nodelist::NodeList;
|
||||
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
||||
use crate::dom::shadowroot::ShadowRoot;
|
||||
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
|
||||
use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
|
||||
use crate::dom::validitystate::{ValidationFlags, ValidityState};
|
||||
|
@ -119,16 +116,6 @@ enum ShadowTree {
|
|||
// TODO: Add shadow trees for other input types (range etc) here
|
||||
}
|
||||
|
||||
const COLOR_TREE_STYLE: &str = "
|
||||
#color-value {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid gray;
|
||||
border-radius: 2px;
|
||||
}
|
||||
";
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#attr-input-type>
|
||||
#[derive(Clone, Copy, Default, JSTraceable, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
|
@ -1064,19 +1051,9 @@ impl HTMLInputElement {
|
|||
/// Return a reference to the ShadowRoot that this element is a host of,
|
||||
/// or create one if none exists.
|
||||
fn shadow_root(&self, can_gc: CanGc) -> DomRoot<ShadowRoot> {
|
||||
self.upcast::<Element>().shadow_root().unwrap_or_else(|| {
|
||||
self.upcast::<Element>()
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed")
|
||||
})
|
||||
self.upcast::<Element>()
|
||||
.shadow_root()
|
||||
.unwrap_or_else(|| self.upcast::<Element>().attach_ua_shadow_root(true, can_gc))
|
||||
}
|
||||
|
||||
fn create_color_shadow_tree(&self, can_gc: CanGc) {
|
||||
|
@ -1085,29 +1062,13 @@ impl HTMLInputElement {
|
|||
Node::replace_all(None, shadow_root.upcast::<Node>(), can_gc);
|
||||
|
||||
let color_value = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||
color_value
|
||||
.upcast::<Element>()
|
||||
.SetId(DOMString::from("color-value"), can_gc);
|
||||
shadow_root
|
||||
.upcast::<Node>()
|
||||
.AppendChild(color_value.upcast::<Node>(), can_gc)
|
||||
.unwrap();
|
||||
|
||||
let style = HTMLStyleElement::new(
|
||||
local_name!("style"),
|
||||
None,
|
||||
&document,
|
||||
None,
|
||||
ElementCreator::ScriptCreated,
|
||||
can_gc,
|
||||
);
|
||||
style
|
||||
color_value
|
||||
.upcast::<Node>()
|
||||
.SetTextContent(Some(DOMString::from(COLOR_TREE_STYLE)), can_gc);
|
||||
shadow_root
|
||||
.upcast::<Node>()
|
||||
.AppendChild(style.upcast::<Node>(), can_gc)
|
||||
.unwrap();
|
||||
.set_implemented_pseudo_element(PseudoElement::ColorSwatch);
|
||||
|
||||
let _ = self
|
||||
.shadow_tree
|
||||
|
|
|
@ -56,9 +56,6 @@ use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConsta
|
|||
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
|
||||
use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
|
||||
|
@ -93,7 +90,6 @@ use crate::dom::mediastream::MediaStream;
|
|||
use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext};
|
||||
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||
use crate::dom::texttrack::TextTrack;
|
||||
use crate::dom::texttracklist::TextTrackList;
|
||||
use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
|
||||
|
@ -2020,17 +2016,11 @@ impl HTMLMediaElement {
|
|||
// if we are already showing the controls.
|
||||
return;
|
||||
}
|
||||
let shadow_root = element
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.unwrap();
|
||||
// FIXME(stevennovaryo): Recheck styling of media element to avoid
|
||||
// reparsing styles.
|
||||
let shadow_root = self
|
||||
.upcast::<Element>()
|
||||
.attach_ua_shadow_root(false, can_gc);
|
||||
let document = self.owner_document();
|
||||
let script = HTMLScriptElement::new(
|
||||
local_name!("script"),
|
||||
|
|
|
@ -14,9 +14,6 @@ use crate::dom::attr::Attr;
|
|||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLMeterElementBinding::HTMLMeterElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
|
@ -27,7 +24,6 @@ use crate::dom::htmldivelement::HTMLDivElement;
|
|||
use crate::dom::htmlelement::HTMLElement;
|
||||
use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits};
|
||||
use crate::dom::nodelist::NodeList;
|
||||
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
|
@ -79,18 +75,7 @@ impl HTMLMeterElement {
|
|||
|
||||
fn create_shadow_tree(&self, can_gc: CanGc) {
|
||||
let document = self.owner_document();
|
||||
let root = self
|
||||
.upcast::<Element>()
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed");
|
||||
let root = self.upcast::<Element>().attach_ua_shadow_root(true, can_gc);
|
||||
|
||||
let meter_value = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||
root.upcast::<Node>()
|
||||
|
|
|
@ -13,9 +13,6 @@ use crate::dom::bindings::cell::DomRefCell;
|
|||
use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLProgressElementBinding::HTMLProgressElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
|
@ -26,7 +23,6 @@ use crate::dom::htmldivelement::HTMLDivElement;
|
|||
use crate::dom::htmlelement::HTMLElement;
|
||||
use crate::dom::node::{BindContext, Node, NodeTraits};
|
||||
use crate::dom::nodelist::NodeList;
|
||||
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
|
@ -77,18 +73,7 @@ impl HTMLProgressElement {
|
|||
|
||||
fn create_shadow_tree(&self, can_gc: CanGc) {
|
||||
let document = self.owner_document();
|
||||
let root = self
|
||||
.upcast::<Element>()
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed");
|
||||
let root = self.upcast::<Element>().attach_ua_shadow_root(true, can_gc);
|
||||
|
||||
let progress_bar = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||
// FIXME: This should use ::-moz-progress-bar
|
||||
|
|
|
@ -28,9 +28,6 @@ use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptio
|
|||
use crate::dom::bindings::codegen::Bindings::HTMLOptionsCollectionBinding::HTMLOptionsCollectionMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
|
||||
ShadowRootMode, SlotAssignmentMode,
|
||||
};
|
||||
use crate::dom::bindings::codegen::GenericBindings::CharacterDataBinding::CharacterData_Binding::CharacterDataMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::{
|
||||
HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement,
|
||||
|
@ -54,7 +51,6 @@ use crate::dom::htmloptionelement::HTMLOptionElement;
|
|||
use crate::dom::htmloptionscollection::HTMLOptionsCollection;
|
||||
use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits, UnbindContext};
|
||||
use crate::dom::nodelist::NodeList;
|
||||
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
|
||||
use crate::dom::validitystate::{ValidationFlags, ValidityState};
|
||||
|
@ -260,18 +256,7 @@ impl HTMLSelectElement {
|
|||
|
||||
fn create_shadow_tree(&self, can_gc: CanGc) {
|
||||
let document = self.owner_document();
|
||||
let root = self
|
||||
.upcast::<Element>()
|
||||
.attach_shadow(
|
||||
IsUserAgentWidget::Yes,
|
||||
ShadowRootMode::Closed,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
SlotAssignmentMode::Manual,
|
||||
can_gc,
|
||||
)
|
||||
.expect("Attaching UA shadow root failed");
|
||||
let root = self.upcast::<Element>().attach_ua_shadow_root(true, can_gc);
|
||||
|
||||
let select_box = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||
select_box.upcast::<Element>().set_string_attribute(
|
||||
|
|
|
@ -48,7 +48,7 @@ use style::attr::AttrValue;
|
|||
use style::context::QuirksMode;
|
||||
use style::dom::OpaqueNode;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::{SelectorImpl, SelectorParser};
|
||||
use style::selector_parser::{PseudoElement, SelectorImpl, SelectorParser};
|
||||
use style::stylesheets::{Stylesheet, UrlExtraData};
|
||||
use uuid::Uuid;
|
||||
use xml5ever::{local_name, serialize as xml_serialize};
|
||||
|
@ -233,6 +233,10 @@ bitflags! {
|
|||
/// Whether this node has a weird parser insertion mode. i.e whether setting innerHTML
|
||||
/// needs extra work or not
|
||||
const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
|
||||
|
||||
/// Whether this node resides in UA shadow DOM. Element within UA Shadow DOM
|
||||
/// will have a different style computation behavior
|
||||
const IS_IN_UA_WIDGET = 1 << 12;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,6 +295,7 @@ impl Node {
|
|||
let parent_is_in_a_document_tree = self.is_in_a_document_tree();
|
||||
let parent_in_shadow_tree = self.is_in_a_shadow_tree();
|
||||
let parent_is_connected = self.is_connected();
|
||||
let parent_is_in_ua_widget = self.is_in_ua_widget();
|
||||
|
||||
for node in new_child.traverse_preorder(ShadowIncluding::No) {
|
||||
if parent_in_shadow_tree {
|
||||
|
@ -305,6 +310,7 @@ impl Node {
|
|||
);
|
||||
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, parent_in_shadow_tree);
|
||||
node.set_flag(NodeFlags::IS_CONNECTED, parent_is_connected);
|
||||
node.set_flag(NodeFlags::IS_IN_UA_WIDGET, parent_is_in_ua_widget);
|
||||
|
||||
// Out-of-document elements never have the descendants flag set.
|
||||
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
|
||||
|
@ -696,6 +702,14 @@ impl Node {
|
|||
self.flags.get().contains(NodeFlags::IS_CONNECTED)
|
||||
}
|
||||
|
||||
pub(crate) fn set_in_ua_widget(&self, in_ua_widget: bool) {
|
||||
self.set_flag(NodeFlags::IS_IN_UA_WIDGET, in_ua_widget)
|
||||
}
|
||||
|
||||
pub(crate) fn is_in_ua_widget(&self) -> bool {
|
||||
self.flags.get().contains(NodeFlags::IS_IN_UA_WIDGET)
|
||||
}
|
||||
|
||||
/// Returns the type ID of this node.
|
||||
pub(crate) fn type_id(&self) -> NodeTypeId {
|
||||
match *self.eventtarget.type_id() {
|
||||
|
@ -1546,6 +1560,20 @@ impl Node {
|
|||
next_node: move |n| n.parent_in_flat_tree(),
|
||||
}
|
||||
}
|
||||
|
||||
/// We are marking this as an implemented pseudo element.
|
||||
pub(crate) fn set_implemented_pseudo_element(&self, pseudo_element: PseudoElement) {
|
||||
// Implemented pseudo element should exist only in the UA shadow DOM.
|
||||
debug_assert!(self.is_in_ua_widget());
|
||||
self.ensure_rare_data().implemented_pseudo_element = Some(pseudo_element);
|
||||
}
|
||||
|
||||
pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||
self.rare_data
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|rare_data| rare_data.implemented_pseudo_element)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
|
||||
|
@ -1630,6 +1658,8 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
|
|||
fn iframe_browsing_context_id(self) -> Option<BrowsingContextId>;
|
||||
fn iframe_pipeline_id(self) -> Option<PipelineId>;
|
||||
fn opaque(self) -> OpaqueNode;
|
||||
fn implemented_pseudo_element(&self) -> Option<PseudoElement>;
|
||||
fn is_in_ua_widget(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<'dom> LayoutDom<'dom, Node> {
|
||||
|
@ -1880,6 +1910,14 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
|||
fn opaque(self) -> OpaqueNode {
|
||||
unsafe { OpaqueNode(self.get_jsobject() as usize) }
|
||||
}
|
||||
|
||||
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||
self.unsafe_get().implemented_pseudo_element()
|
||||
}
|
||||
|
||||
fn is_in_ua_widget(&self) -> bool {
|
||||
self.unsafe_get().is_in_ua_widget()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use euclid::default::Rect;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use stylo_atoms::Atom;
|
||||
|
||||
use crate::dom::bindings::root::{Dom, MutNullableDom};
|
||||
|
@ -47,6 +48,13 @@ pub(crate) struct NodeRareData {
|
|||
|
||||
/// The live list of children return by .childNodes.
|
||||
pub(crate) child_list: MutNullableDom<NodeList>,
|
||||
|
||||
/// Whether this node represents a certain implemented pseudo-element.
|
||||
/// An implemented pseudo-element is a real element within a UA shadow tree
|
||||
/// that will match a certain pseudo-element selector.
|
||||
/// An example of this is the element matching the `::placeholder` selector.
|
||||
#[no_trace]
|
||||
pub(crate) implemented_pseudo_element: Option<PseudoElement>,
|
||||
}
|
||||
|
||||
#[derive(Default, JSTraceable, MallocSizeOf)]
|
||||
|
|
|
@ -201,6 +201,22 @@ impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
|
|||
self.as_node().traversal_parent()
|
||||
}
|
||||
|
||||
fn inheritance_parent(&self) -> Option<Self> {
|
||||
if self.is_pseudo_element() {
|
||||
// The inheritance parent of an implemented pseudo-element should be the
|
||||
// originating element, except if `is_element_backed()` is true, then it should
|
||||
// be the flat tree parent. Note `is_element_backed()` differs from the CSS term.
|
||||
// At the current time, `is_element_backed()` is always false in Servo.
|
||||
//
|
||||
// FIXME: handle the cases of element-backed pseudo-elements.
|
||||
return self.pseudo_element_originating_element();
|
||||
}
|
||||
|
||||
// FIXME: By default the inheritance parent would be the Self::parent_element
|
||||
// but probably we should use the flattened tree parent.
|
||||
self.parent_element()
|
||||
}
|
||||
|
||||
fn is_html_element(&self) -> bool {
|
||||
ServoLayoutElement::is_html_element(self)
|
||||
}
|
||||
|
@ -355,6 +371,21 @@ impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
|
|||
.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
|
||||
}
|
||||
|
||||
/// Whether this element should match user and content rules.
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
self.as_node().node.implemented_pseudo_element()
|
||||
}
|
||||
|
||||
fn store_children_to_process(&self, n: isize) {
|
||||
let data = self.get_style_data().unwrap();
|
||||
data.parallel
|
||||
|
@ -585,6 +616,18 @@ impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
|
|||
self.containing_shadow().map(|s| s.host())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_pseudo_element(&self) -> bool {
|
||||
self.implemented_pseudo_element().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
debug_assert!(self.is_pseudo_element());
|
||||
debug_assert!(!self.matches_user_and_content_rules());
|
||||
self.containing_shadow_host()
|
||||
}
|
||||
|
||||
fn prev_sibling_element(&self) -> Option<Self> {
|
||||
let mut node = self.as_node();
|
||||
while let Some(sibling) = node.prev_sibling() {
|
||||
|
@ -663,18 +706,6 @@ impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
|
|||
self.element.namespace() == other.element.namespace()
|
||||
}
|
||||
|
||||
fn is_pseudo_element(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn match_pseudo_element(
|
||||
&self,
|
||||
_pseudo: &PseudoElement,
|
||||
_context: &mut MatchingContext<Self::Impl>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn match_non_ts_pseudo_class(
|
||||
&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
|
@ -733,6 +764,14 @@ impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
|
|||
}
|
||||
}
|
||||
|
||||
fn match_pseudo_element(
|
||||
&self,
|
||||
pseudo: &PseudoElement,
|
||||
_context: &mut MatchingContext<Self::Impl>,
|
||||
) -> bool {
|
||||
self.implemented_pseudo_element() == Some(*pseudo)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_link(&self) -> bool {
|
||||
match self.as_node().script_type_id() {
|
||||
|
@ -934,21 +973,33 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
|||
::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
|
||||
}
|
||||
|
||||
fn is_pseudo_element(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parent_element(&self) -> Option<Self> {
|
||||
warn!("ServoThreadSafeLayoutElement::parent_element called");
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parent_node_is_shadow_root(&self) -> bool {
|
||||
false
|
||||
self.element.parent_node_is_shadow_root()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn containing_shadow_host(&self) -> Option<Self> {
|
||||
None
|
||||
self.element
|
||||
.containing_shadow_host()
|
||||
.and_then(|element| element.as_node().to_threadsafe().as_element())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_pseudo_element(&self) -> bool {
|
||||
self.element.is_pseudo_element()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
self.element
|
||||
.pseudo_element_originating_element()
|
||||
.and_then(|element| element.as_node().to_threadsafe().as_element())
|
||||
}
|
||||
|
||||
// Skips non-element nodes
|
||||
|
@ -993,14 +1044,6 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
|||
self.element.namespace() == other.element.namespace()
|
||||
}
|
||||
|
||||
fn match_pseudo_element(
|
||||
&self,
|
||||
_pseudo: &PseudoElement,
|
||||
_context: &mut MatchingContext<Self::Impl>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn attr_matches(
|
||||
&self,
|
||||
ns: &NamespaceConstraint<&style::Namespace>,
|
||||
|
@ -1030,6 +1073,14 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
|||
false
|
||||
}
|
||||
|
||||
fn match_pseudo_element(
|
||||
&self,
|
||||
pseudo: &PseudoElement,
|
||||
context: &mut MatchingContext<Self::Impl>,
|
||||
) -> bool {
|
||||
self.element.match_pseudo_element(pseudo, context)
|
||||
}
|
||||
|
||||
fn is_link(&self) -> bool {
|
||||
warn!("ServoThreadSafeLayoutElement::is_link called");
|
||||
false
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue