mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
style: Add bindings for ShadowRoot.
This adds TShadowRoot to the `dom` module. Right now it barely adds uses of it, but this is a prerequisite to fix a bunch of Shadow DOM bugs and separate it from the XBL mess.
This commit is contained in:
parent
df079286c2
commit
2f0df1b421
3 changed files with 118 additions and 38 deletions
|
@ -66,7 +66,7 @@ use style::attr::AttrValue;
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::data::ElementData;
|
use style::data::ElementData;
|
||||||
use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode};
|
use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode};
|
||||||
use style::dom::{TDocument, TElement, TNode};
|
use style::dom::{TDocument, TElement, TNode, TShadowRoot};
|
||||||
use style::element_state::*;
|
use style::element_state::*;
|
||||||
use style::font_metrics::ServoMetricsProvider;
|
use style::font_metrics::ServoMetricsProvider;
|
||||||
use style::properties::{ComputedValues, PropertyDeclarationBlock};
|
use style::properties::{ComputedValues, PropertyDeclarationBlock};
|
||||||
|
@ -150,9 +150,28 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Impossible { }
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'lr ()>);
|
||||||
|
|
||||||
|
impl<'lr> TShadowRoot for ShadowRoot<'lr> {
|
||||||
|
type ConcreteNode = ServoLayoutNode<'lr>;
|
||||||
|
|
||||||
|
fn as_node(&self) -> Self::ConcreteNode {
|
||||||
|
match self.0 { }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn host(&self) -> ServoLayoutElement<'lr> {
|
||||||
|
match self.0 { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'ln> TNode for ServoLayoutNode<'ln> {
|
impl<'ln> TNode for ServoLayoutNode<'ln> {
|
||||||
type ConcreteDocument = ServoLayoutDocument<'ln>;
|
type ConcreteDocument = ServoLayoutDocument<'ln>;
|
||||||
type ConcreteElement = ServoLayoutElement<'ln>;
|
type ConcreteElement = ServoLayoutElement<'ln>;
|
||||||
|
type ConcreteShadowRoot = ShadowRoot<'ln>;
|
||||||
|
|
||||||
fn parent_node(&self) -> Option<Self> {
|
fn parent_node(&self) -> Option<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -208,6 +227,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
|
||||||
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
|
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_shadow_root(&self) -> Option<ShadowRoot<'ln>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn is_in_document(&self) -> bool {
|
fn is_in_document(&self) -> bool {
|
||||||
unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) }
|
unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) }
|
||||||
}
|
}
|
||||||
|
@ -489,11 +512,11 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
.map(|v| String::from(v as &str))
|
.map(|v| String::from(v as &str))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_element_lang(&self,
|
fn match_element_lang(
|
||||||
override_lang: Option<Option<SelectorAttrValue>>,
|
&self,
|
||||||
value: &PseudoClassStringArg)
|
override_lang: Option<Option<SelectorAttrValue>>,
|
||||||
-> bool
|
value: &PseudoClassStringArg,
|
||||||
{
|
) -> bool {
|
||||||
// Servo supports :lang() from CSS Selectors 4, which can take a comma-
|
// Servo supports :lang() from CSS Selectors 4, which can take a comma-
|
||||||
// separated list of language tags in the pseudo-class, and which
|
// separated list of language tags in the pseudo-class, and which
|
||||||
// performs RFC 4647 extended filtering matching on them.
|
// performs RFC 4647 extended filtering matching on them.
|
||||||
|
@ -535,6 +558,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
|
self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shadow_root(&self) -> Option<ShadowRoot<'le>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'le> PartialEq for ServoLayoutElement<'le> {
|
impl<'le> PartialEq for ServoLayoutElement<'le> {
|
||||||
|
|
|
@ -154,6 +154,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
|
||||||
/// The concrete `TDocument` type.
|
/// The concrete `TDocument` type.
|
||||||
type ConcreteDocument: TDocument<ConcreteNode = Self>;
|
type ConcreteDocument: TDocument<ConcreteNode = Self>;
|
||||||
|
|
||||||
|
/// The concrete `TShadowRoot` type.
|
||||||
|
type ConcreteShadowRoot: TShadowRoot<ConcreteNode = Self>;
|
||||||
|
|
||||||
/// Get this node's parent node.
|
/// Get this node's parent node.
|
||||||
fn parent_node(&self) -> Option<Self>;
|
fn parent_node(&self) -> Option<Self>;
|
||||||
|
|
||||||
|
@ -235,6 +238,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
|
||||||
|
|
||||||
/// Get this node as a document, if it's one.
|
/// Get this node as a document, if it's one.
|
||||||
fn as_document(&self) -> Option<Self::ConcreteDocument>;
|
fn as_document(&self) -> Option<Self::ConcreteDocument>;
|
||||||
|
|
||||||
|
/// Get this node as a ShadowRoot, if it's one.
|
||||||
|
fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper to output the subtree rather than the single node when formatting
|
/// Wrapper to output the subtree rather than the single node when formatting
|
||||||
|
@ -314,6 +320,18 @@ fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent:
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The ShadowRoot trait.
|
||||||
|
pub trait TShadowRoot : Sized + Copy + Clone {
|
||||||
|
/// The concrete node type.
|
||||||
|
type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
|
||||||
|
|
||||||
|
/// Get this ShadowRoot as a node.
|
||||||
|
fn as_node(&self) -> Self::ConcreteNode;
|
||||||
|
|
||||||
|
/// Get the shadow host that hosts this ShadowRoot.
|
||||||
|
fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement;
|
||||||
|
}
|
||||||
|
|
||||||
/// The element trait, the main abstraction the style crate acts over.
|
/// The element trait, the main abstraction the style crate acts over.
|
||||||
pub trait TElement
|
pub trait TElement
|
||||||
: Eq
|
: Eq
|
||||||
|
@ -719,6 +737,9 @@ pub trait TElement
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The shadow root this element is a host of.
|
||||||
|
fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
|
||||||
|
|
||||||
/// Return the element which we can use to look up rules in the selector
|
/// Return the element which we can use to look up rules in the selector
|
||||||
/// maps.
|
/// maps.
|
||||||
///
|
///
|
||||||
|
|
|
@ -21,7 +21,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use author_styles::AuthorStyles;
|
use author_styles::AuthorStyles;
|
||||||
use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
|
use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
|
||||||
use data::ElementData;
|
use data::ElementData;
|
||||||
use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode};
|
use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode, TShadowRoot};
|
||||||
use element_state::{ElementState, DocumentState};
|
use element_state::{ElementState, DocumentState};
|
||||||
use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
|
use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
|
||||||
use gecko::data::GeckoStyleSheet;
|
use gecko::data::GeckoStyleSheet;
|
||||||
|
@ -131,6 +131,24 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple wrapper over `ShadowRoot`.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
|
||||||
|
|
||||||
|
impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
|
||||||
|
type ConcreteNode = GeckoNode<'lr>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_node(&self) -> Self::ConcreteNode {
|
||||||
|
GeckoNode(&self.0._base._base._base._base)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn host(&self) -> GeckoElement<'lr> {
|
||||||
|
GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
|
/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
|
||||||
///
|
///
|
||||||
/// Important: We don't currently refcount the DOM, because the wrapper lifetime
|
/// Important: We don't currently refcount the DOM, because the wrapper lifetime
|
||||||
|
@ -171,6 +189,11 @@ impl<'ln> GeckoNode<'ln> {
|
||||||
self.node_info().mInner.mNodeType == DOCUMENT_NODE
|
self.node_info().mInner.mNodeType == DOCUMENT_NODE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_shadow_root(&self) -> bool {
|
||||||
|
self.is_in_shadow_tree() && self.parent_node().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_content(content: &'ln nsIContent) -> Self {
|
fn from_content(content: &'ln nsIContent) -> Self {
|
||||||
GeckoNode(&content._base)
|
GeckoNode(&content._base)
|
||||||
|
@ -205,6 +228,13 @@ impl<'ln> GeckoNode<'ln> {
|
||||||
self.bool_flags() & (1u32 << flag as u32) != 0
|
self.bool_flags() & (1u32 << flag as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This logic is duplicate in Gecko's nsINode::IsInShadowTree().
|
||||||
|
#[inline]
|
||||||
|
fn is_in_shadow_tree(&self) -> bool {
|
||||||
|
use gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
|
||||||
|
self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0
|
||||||
|
}
|
||||||
|
|
||||||
/// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent.
|
/// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent.
|
||||||
/// Make sure to mirror any modifications in both places.
|
/// Make sure to mirror any modifications in both places.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -275,6 +305,7 @@ impl<'ln> NodeInfo for GeckoNode<'ln> {
|
||||||
|
|
||||||
impl<'ln> TNode for GeckoNode<'ln> {
|
impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
type ConcreteDocument = GeckoDocument<'ln>;
|
type ConcreteDocument = GeckoDocument<'ln>;
|
||||||
|
type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
|
||||||
type ConcreteElement = GeckoElement<'ln>;
|
type ConcreteElement = GeckoElement<'ln>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -329,20 +360,33 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_element(&self) -> Option<GeckoElement<'ln>> {
|
fn as_element(&self) -> Option<GeckoElement<'ln>> {
|
||||||
if self.is_element() {
|
if !self.is_element() {
|
||||||
unsafe { Some(GeckoElement(&*(self.0 as *const _ as *const RawGeckoElement))) }
|
return None;
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(GeckoElement(unsafe {
|
||||||
|
&*(self.0 as *const _ as *const RawGeckoElement)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_document(&self) -> Option<Self::ConcreteDocument> {
|
fn as_document(&self) -> Option<Self::ConcreteDocument> {
|
||||||
if self.is_document() {
|
if !self.is_document() {
|
||||||
Some(self.owner_doc())
|
return None;
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(self.owner_doc())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
|
||||||
|
if !self.is_shadow_root() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(GeckoShadowRoot(unsafe {
|
||||||
|
&*(self.0 as *const _ as *const structs::ShadowRoot)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,11 +532,7 @@ impl<'le> GeckoElement<'le> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn flags(&self) -> u32 {
|
fn flags(&self) -> u32 {
|
||||||
self.raw_node()._base._base_1.mFlags
|
self.as_node().flags()
|
||||||
}
|
|
||||||
|
|
||||||
fn raw_node(&self) -> &RawGeckoNode {
|
|
||||||
&(self.0)._base._base._base
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: We can implement this without OOL calls, but we can't easily given
|
// FIXME: We can implement this without OOL calls, but we can't easily given
|
||||||
|
@ -517,13 +557,6 @@ impl<'le> GeckoElement<'le> {
|
||||||
self.flags() & (NODE_NEEDS_FRAME as u32) != 0
|
self.flags() & (NODE_NEEDS_FRAME as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this element has a shadow root.
|
|
||||||
#[inline]
|
|
||||||
fn shadow_root(&self) -> Option<&structs::ShadowRoot> {
|
|
||||||
let slots = self.extended_slots()?;
|
|
||||||
unsafe { slots.mShadowRoot.mRawPtr.as_ref() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the DOM slots for this Element, if they exist.
|
/// Returns a reference to the DOM slots for this Element, if they exist.
|
||||||
fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
|
fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
|
||||||
let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
|
let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
|
||||||
|
@ -732,18 +765,11 @@ impl<'le> GeckoElement<'le> {
|
||||||
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
|
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This logic is duplicate in Gecko's nsIContent::IsInShadowTree().
|
|
||||||
#[inline]
|
|
||||||
fn is_in_shadow_tree(&self) -> bool {
|
|
||||||
use gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
|
|
||||||
self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
|
/// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_in_anonymous_subtree(&self) -> bool {
|
fn is_in_anonymous_subtree(&self) -> bool {
|
||||||
self.is_in_native_anonymous_subtree() ||
|
self.is_in_native_anonymous_subtree() ||
|
||||||
(!self.is_in_shadow_tree() && self.has_xbl_binding_parent())
|
(!self.as_node().is_in_shadow_tree() && self.has_xbl_binding_parent())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn css_transitions_info(&self) -> FnvHashMap<LonghandId, Arc<AnimationValue>> {
|
fn css_transitions_info(&self) -> FnvHashMap<LonghandId, Arc<AnimationValue>> {
|
||||||
|
@ -1011,7 +1037,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
/// Return the list of slotted nodes of this node.
|
/// Return the list of slotted nodes of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
|
fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
|
||||||
if !self.is_html_slot_element() || !self.is_in_shadow_tree() {
|
if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {
|
||||||
return &[];
|
return &[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,6 +1062,12 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
unsafe { mem::transmute(assigned_nodes) }
|
unsafe { mem::transmute(assigned_nodes) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {
|
||||||
|
let slots = self.extended_slots()?;
|
||||||
|
unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute `f` for each anonymous content child element (apart from
|
/// Execute `f` for each anonymous content child element (apart from
|
||||||
/// ::before and ::after) whose originating element is `self`.
|
/// ::before and ::after) whose originating element is `self`.
|
||||||
fn each_anonymous_content_child<F>(&self, mut f: F)
|
fn each_anonymous_content_child<F>(&self, mut f: F)
|
||||||
|
@ -1416,9 +1448,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
// That will allow to clean up a bunch in
|
// That will allow to clean up a bunch in
|
||||||
// push_applicable_declarations.
|
// push_applicable_declarations.
|
||||||
if let Some(shadow) = element.shadow_root() {
|
if let Some(shadow) = element.shadow_root() {
|
||||||
debug_assert!(!shadow.mServoStyles.mPtr.is_null());
|
debug_assert!(!shadow.0.mServoStyles.mPtr.is_null());
|
||||||
let author_styles = unsafe {
|
let author_styles = unsafe {
|
||||||
&*(shadow.mServoStyles.mPtr
|
&*(shadow.0.mServoStyles.mPtr
|
||||||
as *const structs::RawServoAuthorStyles
|
as *const structs::RawServoAuthorStyles
|
||||||
as *const bindings::RawServoAuthorStyles)
|
as *const bindings::RawServoAuthorStyles)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue