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:
Emilio Cobos Álvarez 2018-03-03 22:14:36 +01:00
parent df079286c2
commit 2f0df1b421
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 118 additions and 38 deletions

View file

@ -66,7 +66,7 @@ use style::attr::AttrValue;
use style::context::SharedStyleContext;
use style::data::ElementData;
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::font_metrics::ServoMetricsProvider;
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> {
type ConcreteDocument = ServoLayoutDocument<'ln>;
type ConcreteElement = ServoLayoutElement<'ln>;
type ConcreteShadowRoot = ShadowRoot<'ln>;
fn parent_node(&self) -> Option<Self> {
unsafe {
@ -208,6 +227,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
}
fn as_shadow_root(&self) -> Option<ShadowRoot<'ln>> {
None
}
fn is_in_document(&self) -> bool {
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))
}
fn match_element_lang(&self,
override_lang: Option<Option<SelectorAttrValue>>,
value: &PseudoClassStringArg)
-> bool
{
fn match_element_lang(
&self,
override_lang: Option<Option<SelectorAttrValue>>,
value: &PseudoClassStringArg,
) -> bool {
// Servo supports :lang() from CSS Selectors 4, which can take a comma-
// separated list of language tags in the pseudo-class, and which
// 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);
}
}
fn shadow_root(&self) -> Option<ShadowRoot<'le>> {
None
}
}
impl<'le> PartialEq for ServoLayoutElement<'le> {

View file

@ -154,6 +154,9 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
/// The concrete `TDocument` type.
type ConcreteDocument: TDocument<ConcreteNode = Self>;
/// The concrete `TShadowRoot` type.
type ConcreteShadowRoot: TShadowRoot<ConcreteNode = Self>;
/// Get this node's parent node.
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.
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
@ -314,6 +320,18 @@ fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent:
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.
pub trait TElement
: Eq
@ -719,6 +737,9 @@ pub trait TElement
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
/// maps.
///

View file

@ -21,7 +21,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use author_styles::AuthorStyles;
use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
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 font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
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.
///
/// 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
}
#[inline]
fn is_shadow_root(&self) -> bool {
self.is_in_shadow_tree() && self.parent_node().is_none()
}
#[inline]
fn from_content(content: &'ln nsIContent) -> Self {
GeckoNode(&content._base)
@ -205,6 +228,13 @@ impl<'ln> GeckoNode<'ln> {
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.
/// Make sure to mirror any modifications in both places.
#[inline]
@ -275,6 +305,7 @@ impl<'ln> NodeInfo for GeckoNode<'ln> {
impl<'ln> TNode for GeckoNode<'ln> {
type ConcreteDocument = GeckoDocument<'ln>;
type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
type ConcreteElement = GeckoElement<'ln>;
#[inline]
@ -329,20 +360,33 @@ impl<'ln> TNode for GeckoNode<'ln> {
#[inline]
fn as_element(&self) -> Option<GeckoElement<'ln>> {
if self.is_element() {
unsafe { Some(GeckoElement(&*(self.0 as *const _ as *const RawGeckoElement))) }
} else {
None
if !self.is_element() {
return None;
}
Some(GeckoElement(unsafe {
&*(self.0 as *const _ as *const RawGeckoElement)
}))
}
#[inline]
fn as_document(&self) -> Option<Self::ConcreteDocument> {
if self.is_document() {
Some(self.owner_doc())
} else {
None
if !self.is_document() {
return 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]
fn flags(&self) -> u32 {
self.raw_node()._base._base_1.mFlags
}
fn raw_node(&self) -> &RawGeckoNode {
&(self.0)._base._base._base
self.as_node().flags()
}
// 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
}
/// 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.
fn dom_slots(&self) -> Option<&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
}
/// 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.
#[inline]
fn is_in_anonymous_subtree(&self) -> bool {
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>> {
@ -1011,7 +1037,7 @@ impl<'le> TElement for GeckoElement<'le> {
/// Return the list of slotted nodes of this node.
#[inline]
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 &[];
}
@ -1036,6 +1062,12 @@ impl<'le> TElement for GeckoElement<'le> {
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
/// ::before and ::after) whose originating element is `self`.
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
// push_applicable_declarations.
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 {
&*(shadow.mServoStyles.mPtr
&*(shadow.0.mServoStyles.mPtr
as *const structs::RawServoAuthorStyles
as *const bindings::RawServoAuthorStyles)
};