Skip to content

Commit

Permalink
Auto merge of #22743 - ferjm:shadowdom, r=emilio
Browse files Browse the repository at this point in the history
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 -->
  • Loading branch information
bors-servo committed Apr 29, 2019
2 parents d58ea97 + 37e88e7 commit 30ded91
Show file tree
Hide file tree
Showing 75 changed files with 2,080 additions and 701 deletions.
3 changes: 3 additions & 0 deletions components/config/prefs.rs
Expand Up @@ -222,6 +222,9 @@ mod gen {
enabled: bool,
}
},
shadowdom: {
enabled: bool,
},
svg: {
enabled: bool,
},
Expand Down
2 changes: 1 addition & 1 deletion components/layout/query.rs
Expand Up @@ -1050,7 +1050,7 @@ fn inner_text_collection_steps<N: LayoutNode>(

// Step 3.
let display = style.get_box().display;
if !child.is_in_document() || display == Display::None {
if !child.is_connected() || display == Display::None {
continue;
}

Expand Down
156 changes: 132 additions & 24 deletions components/layout_thread/dom_wrapper.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
}
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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>> {
Expand Down Expand Up @@ -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))
}

Expand Down
14 changes: 13 additions & 1 deletion components/layout_thread/lib.rs
Expand Up @@ -103,7 +103,7 @@ use std::time::Duration;
use style::animation::Animation;
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo};
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode};
use style::driver;
use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
Expand Down Expand Up @@ -1346,6 +1346,18 @@ impl LayoutThread {
}
}

debug!(
"Shadow roots in document {:?}",
document.shadow_roots().len()
);

// Flush shadow roots stylesheets if dirty.
document.flush_shadow_roots_stylesheets(
&self.stylist.device(),
document.quirks_mode(),
guards.author.clone(),
);

let restyles = document.drain_pending_restyles();
debug!("Draining restyles: {}", restyles.len());

Expand Down
6 changes: 6 additions & 0 deletions components/malloc_size_of/lib.rs
Expand Up @@ -778,6 +778,12 @@ impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf
}
}

impl MallocSizeOf for selectors::context::QuirksMode {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}

impl MallocSizeOf for Void {
#[inline]
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
Expand Down
4 changes: 2 additions & 2 deletions components/script/devtools.rs
Expand Up @@ -15,7 +15,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::document::AnimationFrameCallback;
use crate::dom::element::Element;
use crate::dom::globalscope::GlobalScope;
use crate::dom::node::{window_from_node, Node};
use crate::dom::node::{window_from_node, Node, ShadowIncluding};
use crate::dom::window::Window;
use crate::script_thread::Documents;
use devtools_traits::TimelineMarkerType;
Expand Down Expand Up @@ -103,7 +103,7 @@ fn find_node_by_unique_id(
documents.find_document(pipeline).and_then(|document| {
document
.upcast::<Node>()
.traverse_preorder()
.traverse_preorder(ShadowIncluding::Yes)
.find(|candidate| candidate.unique_id() == node_id)
})
}
Expand Down
4 changes: 2 additions & 2 deletions components/script/dom/bindings/htmlconstructor.rs
Expand Up @@ -72,8 +72,8 @@ use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::root::DomRoot;
use crate::dom::create::create_native_html_element;
use crate::dom::customelementregistry::ConstructionStackEntry;
use crate::dom::element::{CustomElementState, Element, ElementCreator};
use crate::dom::customelementregistry::{ConstructionStackEntry, CustomElementState};
use crate::dom::element::{Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::window::Window;
use crate::script_thread::ScriptThread;
Expand Down

0 comments on commit 30ded91

Please sign in to comment.