mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
Implement Gecko's pseudo element matching
Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
1a3dd44f78
commit
2a87b7b560
6 changed files with 69 additions and 20 deletions
|
@ -19,26 +19,26 @@ textarea {
|
||||||
font-size: 0.8333em;
|
font-size: 0.8333em;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-servo-text-control-inner-editor {
|
input::-servo-text-control-inner-editor {
|
||||||
overflow-wrap: normal;
|
overflow-wrap: normal;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-servo-text-control-inner-container {
|
input::-servo-text-control-inner-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-servo-text-control-inner-editor, ::-servo-text-control-placeholder {
|
input::-servo-text-control-inner-editor, input::-servo-text-control-placeholder {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
margin-block: auto !important;
|
margin-block: auto !important;
|
||||||
inset-block: 0 !important;
|
inset-block: 0 !important;
|
||||||
block-size: fit-content !important;
|
block-size: fit-content !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-servo-text-control-placeholder {
|
input::-servo-text-control-placeholder {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
color: grey !important;
|
color: grey !important;
|
||||||
|
|
|
@ -666,6 +666,10 @@ impl Element {
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.set_containing_shadow_root(Some(&shadow_root));
|
.set_containing_shadow_root(Some(&shadow_root));
|
||||||
|
|
||||||
|
if is_ua_widget == IsUserAgentWidget::Yes {
|
||||||
|
shadow_root.upcast::<Node>().set_in_ua_widget();
|
||||||
|
}
|
||||||
|
|
||||||
let bind_context = BindContext {
|
let bind_context = BindContext {
|
||||||
tree_connected: self.upcast::<Node>().is_connected(),
|
tree_connected: self.upcast::<Node>().is_connected(),
|
||||||
tree_is_in_a_document_tree: self.upcast::<Node>().is_in_a_document_tree(),
|
tree_is_in_a_document_tree: self.upcast::<Node>().is_in_a_document_tree(),
|
||||||
|
|
|
@ -231,9 +231,12 @@ bitflags! {
|
||||||
/// needs extra work or not
|
/// needs extra work or not
|
||||||
const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
|
const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
|
||||||
|
|
||||||
|
/// Whether this node reside in UA shadow DOM.
|
||||||
|
const IS_IN_UA_WIDGET = 1 << 12;
|
||||||
|
|
||||||
/// Whether this node serves as the text container for editable content of
|
/// Whether this node serves as the text container for editable content of
|
||||||
/// <input> or <textarea> element.
|
/// <input> or <textarea> element.
|
||||||
const IS_TEXT_CONTROL_INNER_EDITOR = 1 << 12;
|
const IS_TEXT_CONTROL_INNER_EDITOR = 1 << 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +295,7 @@ impl Node {
|
||||||
let parent_is_in_a_document_tree = self.is_in_a_document_tree();
|
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_in_shadow_tree = self.is_in_a_shadow_tree();
|
||||||
let parent_is_connected = self.is_connected();
|
let parent_is_connected = self.is_connected();
|
||||||
|
let parent_is_in_ua_widget = self.in_ua_widget();
|
||||||
|
|
||||||
for node in new_child.traverse_preorder(ShadowIncluding::No) {
|
for node in new_child.traverse_preorder(ShadowIncluding::No) {
|
||||||
if parent_in_shadow_tree {
|
if parent_in_shadow_tree {
|
||||||
|
@ -306,6 +310,7 @@ impl Node {
|
||||||
);
|
);
|
||||||
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, parent_in_shadow_tree);
|
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_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.
|
// Out-of-document elements never have the descendants flag set.
|
||||||
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
|
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
|
||||||
|
@ -711,6 +716,14 @@ impl Node {
|
||||||
.contains(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR)
|
.contains(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_in_ua_widget(&self) {
|
||||||
|
self.set_flag(NodeFlags::IS_IN_UA_WIDGET, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn in_ua_widget(&self) -> bool {
|
||||||
|
self.flags.get().contains(NodeFlags::IS_IN_UA_WIDGET)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the type ID of this node.
|
/// Returns the type ID of this node.
|
||||||
pub(crate) fn type_id(&self) -> NodeTypeId {
|
pub(crate) fn type_id(&self) -> NodeTypeId {
|
||||||
match *self.eventtarget.type_id() {
|
match *self.eventtarget.type_id() {
|
||||||
|
@ -1664,6 +1677,7 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
|
||||||
fn iframe_pipeline_id(self) -> Option<PipelineId>;
|
fn iframe_pipeline_id(self) -> Option<PipelineId>;
|
||||||
fn opaque(self) -> OpaqueNode;
|
fn opaque(self) -> OpaqueNode;
|
||||||
fn pseudo_element(&self) -> Option<PseudoElement>;
|
fn pseudo_element(&self) -> Option<PseudoElement>;
|
||||||
|
fn in_ua_widget(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dom> LayoutDom<'dom, Node> {
|
impl<'dom> LayoutDom<'dom, Node> {
|
||||||
|
@ -1937,13 +1951,16 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
unsafe { OpaqueNode(self.get_jsobject() as usize) }
|
unsafe { OpaqueNode(self.get_jsobject() as usize) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn pseudo_element(&self) -> Option<PseudoElement> {
|
fn pseudo_element(&self) -> Option<PseudoElement> {
|
||||||
self.unsafe_get()
|
self.unsafe_get()
|
||||||
.rare_data()
|
.rare_data()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|data| data.pseudo_element)
|
.and_then(|data| data.pseudo_element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_ua_widget(&self) -> bool {
|
||||||
|
self.unsafe_get().in_ua_widget()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -47,7 +47,7 @@ use crate::script_runtime::CanGc;
|
||||||
use crate::stylesheet_set::StylesheetSetRef;
|
use crate::stylesheet_set::StylesheetSetRef;
|
||||||
|
|
||||||
/// Whether a shadow root hosts an User Agent widget.
|
/// Whether a shadow root hosts an User Agent widget.
|
||||||
#[derive(JSTraceable, MallocSizeOf, PartialEq)]
|
#[derive(JSTraceable, MallocSizeOf, PartialEq, Clone, Copy)]
|
||||||
pub(crate) enum IsUserAgentWidget {
|
pub(crate) enum IsUserAgentWidget {
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
|
|
|
@ -327,6 +327,16 @@ impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
|
||||||
.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
|
.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn matches_user_and_content_rules(&self) -> bool {
|
||||||
|
!self.as_node().node.in_ua_widget()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||||
|
self.as_node().node.pseudo_element()
|
||||||
|
}
|
||||||
|
|
||||||
fn store_children_to_process(&self, n: isize) {
|
fn store_children_to_process(&self, n: isize) {
|
||||||
let data = self.get_style_data().unwrap();
|
let data = self.get_style_data().unwrap();
|
||||||
data.parallel
|
data.parallel
|
||||||
|
@ -631,16 +641,27 @@ impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
|
||||||
self.element.namespace() == other.element.namespace()
|
self.element.namespace() == other.element.namespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn is_pseudo_element(&self) -> bool {
|
fn is_pseudo_element(&self) -> bool {
|
||||||
false
|
self.element.upcast::<Node>().pseudo_element().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||||
|
debug_assert!(self.is_pseudo_element());
|
||||||
|
if self.element.upcast::<Node>().in_ua_widget() {
|
||||||
|
self.containing_shadow_host()
|
||||||
|
} else {
|
||||||
|
self.parent_element()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_pseudo_element(
|
fn match_pseudo_element(
|
||||||
&self,
|
&self,
|
||||||
_pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
_context: &mut MatchingContext<Self::Impl>,
|
_context: &mut MatchingContext<Self::Impl>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
self.element.upcast::<Node>().pseudo_element() == Some(*pseudo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_non_ts_pseudo_class(
|
fn match_non_ts_pseudo_class(
|
||||||
|
@ -893,8 +914,9 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
||||||
::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
|
::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn is_pseudo_element(&self) -> bool {
|
fn is_pseudo_element(&self) -> bool {
|
||||||
false
|
self.element.is_pseudo_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_element(&self) -> Option<Self> {
|
fn parent_element(&self) -> Option<Self> {
|
||||||
|
@ -903,11 +925,13 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_node_is_shadow_root(&self) -> bool {
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
false
|
self.element.parent_node_is_shadow_root()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn containing_shadow_host(&self) -> Option<Self> {
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
None
|
self.element
|
||||||
|
.containing_shadow_host()
|
||||||
|
.and_then(|element| element.as_node().to_threadsafe().as_element())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skips non-element nodes
|
// Skips non-element nodes
|
||||||
|
@ -952,12 +976,20 @@ impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
|
||||||
self.element.namespace() == other.element.namespace()
|
self.element.namespace() == other.element.namespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||||
|
debug_assert!(self.is_pseudo_element());
|
||||||
|
self.element
|
||||||
|
.pseudo_element_originating_element()
|
||||||
|
.and_then(|element| element.as_node().to_threadsafe().as_element())
|
||||||
|
}
|
||||||
|
|
||||||
fn match_pseudo_element(
|
fn match_pseudo_element(
|
||||||
&self,
|
&self,
|
||||||
_pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
_context: &mut MatchingContext<Self::Impl>,
|
_context: &mut MatchingContext<Self::Impl>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
self.element.implemented_pseudo_element() == Some(*pseudo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attr_matches(
|
fn attr_matches(
|
||||||
|
|
|
@ -244,10 +244,7 @@ pub struct ServoThreadSafeLayoutNode<'dom> {
|
||||||
impl<'dom> ServoThreadSafeLayoutNode<'dom> {
|
impl<'dom> ServoThreadSafeLayoutNode<'dom> {
|
||||||
/// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
|
/// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
|
||||||
pub fn new(node: ServoLayoutNode<'dom>) -> Self {
|
pub fn new(node: ServoLayoutNode<'dom>) -> Self {
|
||||||
ServoThreadSafeLayoutNode {
|
ServoThreadSafeLayoutNode { node, pseudo: None }
|
||||||
node,
|
|
||||||
pseudo: node.node.pseudo_element(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to
|
/// Returns the interior of this node as a `LayoutDom`. This is highly unsafe for layout to
|
||||||
|
@ -443,7 +440,6 @@ pub struct ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dom> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
|
impl<'dom> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
|
||||||
// MYNOTES: This seems to be unused anywhere.
|
|
||||||
pub fn new(parent: ServoThreadSafeLayoutNode<'dom>) -> Self {
|
pub fn new(parent: ServoThreadSafeLayoutNode<'dom>) -> Self {
|
||||||
let first_child = match parent.pseudo_element() {
|
let first_child = match parent.pseudo_element() {
|
||||||
None => parent
|
None => parent
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue