diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-04-29 08:38:50 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-29 08:38:50 -0400 |
commit | 799490a02e9bea575bf34c39f045ef0883539f05 (patch) | |
tree | 278cada683564db36997cf3f96c92513b89c3096 /components/layout_thread/dom_wrapper.rs | |
parent | d58ea974baff1b51a43d2e2bf4b287ff11991a8d (diff) | |
parent | 37e88e77cdf00e3555599dd4004d03548bd95dcf (diff) | |
download | servo-799490a02e9bea575bf34c39f045ef0883539f05.tar.gz servo-799490a02e9bea575bf34c39f045ef0883539f05.zip |
Auto merge of #22743 - ferjm:shadowdom, r=emilio
Partial ShadowDOM support
This is just an early WIP, not to take it very seriously yet.
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [x] These changes fix #22719
- [x] There are tests for these changes
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22743)
<!-- Reviewable:end -->
Diffstat (limited to 'components/layout_thread/dom_wrapper.rs')
-rw-r--r-- | components/layout_thread/dom_wrapper.rs | 156 |
1 files changed, 132 insertions, 24 deletions
diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 14c09923aec..88eedef7d17 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -40,13 +40,16 @@ use net_traits::image::base::{Image, ImageMetadata}; use range::Range; use script::layout_exports::NodeFlags; use script::layout_exports::PendingRestyle; +use script::layout_exports::ShadowRoot; use script::layout_exports::{ - CharacterDataTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId, TextTypeId, + CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId, + TextTypeId, }; use script::layout_exports::{Document, Element, Node, Text}; use script::layout_exports::{LayoutCharacterDataHelpers, LayoutDocumentHelpers}; use script::layout_exports::{ - LayoutDom, LayoutElementHelpers, LayoutNodeHelpers, RawLayoutElementHelpers, + LayoutDom, LayoutElementHelpers, LayoutNodeHelpers, LayoutShadowRootHelpers, + RawLayoutElementHelpers, }; use script_layout_interface::wrapper_traits::{ DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode, @@ -80,10 +83,13 @@ use style::dom::{DomChildren, LayoutIterator, NodeInfo, OpaqueNode}; use style::dom::{TDocument, TElement, TNode, TShadowRoot}; use style::element_state::*; use style::font_metrics::ServoMetricsProvider; +use style::media_queries::Device; use style::properties::{ComputedValues, PropertyDeclarationBlock}; use style::selector_parser::{extended_filtering, PseudoElement, SelectorImpl}; use style::selector_parser::{AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass}; -use style::shared_lock::{Locked as StyleLocked, SharedRwLock as StyleSharedRwLock}; +use style::shared_lock::{ + Locked as StyleLocked, SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard, +}; use style::str::is_whitespace; use style::stylist::CascadeData; use style::CaseSensitivityExt; @@ -161,39 +167,72 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> { } #[derive(Clone, Copy, PartialEq)] -enum Impossible {} +pub struct ServoShadowRoot<'a> { + /// The wrapped shadow root. + shadow_root: LayoutDom<ShadowRoot>, -#[derive(Clone, Copy, PartialEq)] -pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'lr ()>); + /// Being chained to a PhantomData prevents `ShadowRoot`s from escaping. + chain: PhantomData<&'a ()>, +} -impl<'lr> TShadowRoot for ShadowRoot<'lr> { +impl<'lr> Debug for ServoShadowRoot<'lr> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.as_node().fmt(f) + } +} + +impl<'lr> TShadowRoot for ServoShadowRoot<'lr> { type ConcreteNode = ServoLayoutNode<'lr>; fn as_node(&self) -> Self::ConcreteNode { - match self.0 {} + ServoLayoutNode::from_layout_js(self.shadow_root.upcast()) } fn host(&self) -> ServoLayoutElement<'lr> { - match self.0 {} + ServoLayoutElement::from_layout_js(unsafe { self.shadow_root.get_host_for_layout() }) } fn style_data<'a>(&self) -> Option<&'a CascadeData> where Self: 'a, { - match self.0 {} + Some(unsafe { + &self + .shadow_root + .get_style_data_for_layout::<ServoLayoutElement>() + .data + }) + } +} + +impl<'lr> ServoShadowRoot<'lr> { + fn from_layout_js(shadow_root: LayoutDom<ShadowRoot>) -> ServoShadowRoot<'lr> { + ServoShadowRoot { + shadow_root, + chain: PhantomData, + } + } + + pub unsafe fn flush_stylesheets( + &self, + device: &Device, + quirks_mode: QuirksMode, + guard: &SharedRwLockReadGuard, + ) { + self.shadow_root + .flush_stylesheets::<ServoLayoutElement>(device, quirks_mode, guard) } } impl<'ln> TNode for ServoLayoutNode<'ln> { type ConcreteDocument = ServoLayoutDocument<'ln>; type ConcreteElement = ServoLayoutElement<'ln>; - type ConcreteShadowRoot = ShadowRoot<'ln>; + type ConcreteShadowRoot = ServoShadowRoot<'ln>; fn parent_node(&self) -> Option<Self> { unsafe { self.node - .parent_node_ref() + .composed_parent_node_ref() .map(|node| self.new_with_this_lifetime(&node)) } } @@ -235,7 +274,11 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { } fn traversal_parent(&self) -> Option<ServoLayoutElement<'ln>> { - self.parent_element() + let parent = self.parent_node()?; + if let Some(shadow) = parent.as_shadow_root() { + return Some(shadow.host()); + }; + parent.as_element() } fn opaque(&self) -> OpaqueNode { @@ -256,8 +299,8 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { .map(ServoLayoutDocument::from_layout_js) } - fn as_shadow_root(&self) -> Option<ShadowRoot<'ln>> { - None + fn as_shadow_root(&self) -> Option<ServoShadowRoot<'ln>> { + self.node.downcast().map(ServoShadowRoot::from_layout_js) } fn is_in_document(&self) -> bool { @@ -293,6 +336,10 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { unsafe fn take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData { self.get_jsmanaged().take_style_and_layout_data() } + + fn is_connected(&self) -> bool { + unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) } + } } impl<'ln> GetLayoutData for ServoLayoutNode<'ln> { @@ -378,6 +425,36 @@ impl<'ld> ServoLayoutDocument<'ld> { unsafe { self.document.style_shared_lock() } } + pub fn shadow_roots(&self) -> Vec<ServoShadowRoot> { + unsafe { + self.document + .shadow_roots() + .iter() + .map(|sr| { + debug_assert!(sr.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED)); + ServoShadowRoot::from_layout_js(*sr) + }) + .collect() + } + } + + pub fn flush_shadow_roots_stylesheets( + &self, + device: &Device, + quirks_mode: QuirksMode, + guard: &SharedRwLockReadGuard, + ) { + unsafe { + if !self.document.shadow_roots_styles_changed() { + return; + } + self.document.flush_shadow_roots_stylesheets(); + for shadow_root in self.shadow_roots() { + shadow_root.flush_stylesheets(device, quirks_mode, guard); + } + } + } + pub fn from_layout_js(doc: LayoutDom<Document>) -> ServoLayoutDocument<'ld> { ServoLayoutDocument { document: doc, @@ -414,7 +491,11 @@ impl<'le> TElement for ServoLayoutElement<'le> { } fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> { - LayoutIterator(self.as_node().dom_children()) + LayoutIterator(if let Some(shadow) = self.shadow_root() { + shadow.as_node().dom_children() + } else { + self.as_node().dom_children() + }) } fn is_html_element(&self) -> bool { @@ -488,7 +569,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } unsafe fn set_dirty_descendants(&self) { - debug_assert!(self.as_node().is_in_document()); + debug_assert!(self.as_node().is_connected()); self.as_node() .node .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true) @@ -616,12 +697,23 @@ impl<'le> TElement for ServoLayoutElement<'le> { } } - fn shadow_root(&self) -> Option<ShadowRoot<'le>> { - None + /// The shadow root this element is a host of. + fn shadow_root(&self) -> Option<ServoShadowRoot<'le>> { + unsafe { + self.element + .get_shadow_root_for_layout() + .map(ServoShadowRoot::from_layout_js) + } } - fn containing_shadow(&self) -> Option<ShadowRoot<'le>> { - None + /// The shadow root which roots the subtree this element is contained in. + fn containing_shadow(&self) -> Option<ServoShadowRoot<'le>> { + unsafe { + self.element + .upcast() + .containing_shadow_root_for_layout() + .map(ServoShadowRoot::from_layout_js) + } } } @@ -702,15 +794,26 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn parent_element(&self) -> Option<ServoLayoutElement<'le>> { - unsafe { self.element.upcast().parent_node_ref().and_then(as_element) } + unsafe { + self.element + .upcast() + .composed_parent_node_ref() + .and_then(as_element) + } } fn parent_node_is_shadow_root(&self) -> bool { - false + match self.as_node().parent_node() { + None => false, + Some(node) => { + node.script_type_id() == + NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) + }, + } } fn containing_shadow_host(&self) -> Option<Self> { - None + self.containing_shadow().map(|s| s.host()) } fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> { @@ -985,6 +1088,11 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { } fn children(&self) -> LayoutIterator<Self::ChildrenIterator> { + if let Some(shadow) = self.node.as_element().and_then(|e| e.shadow_root()) { + return LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new( + shadow.as_node().to_threadsafe(), + )); + } LayoutIterator(ThreadSafeLayoutNodeChildrenIterator::new(*self)) } |