Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial ShadowDOM support #22743

Merged
merged 83 commits into from Apr 29, 2019
Merged
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
cbcf21c
DocumentOrShadowRoot mixin
ferjm Jan 18, 2019
18ae0fc
ShadowRoot interface
ferjm Jan 21, 2019
4304ee2
Partial ShadowRoot implementation of DocumentOrShadowRoot
ferjm Jan 21, 2019
569b4fc
Element attachShadow implementation
ferjm Jan 22, 2019
9022bd3
IS_IN_SHADOW_TREE flag
ferjm Jan 23, 2019
091fcbe
Node shadow root owner
ferjm Jan 23, 2019
ffdc9d2
Expose Element.AttachShadow under dom.shadowdom.enabled pref
ferjm Jan 23, 2019
f3e7073
Test DOM isolation for shadow trees
ferjm Jan 24, 2019
4897584
Unify DocumentOrShadowRoot implementation
ferjm Jan 24, 2019
640fc04
Implement shadow-including root, set node as in doc when connected. M…
ferjm Jan 25, 2019
441357b
Add is_connected flag to node and use it to replace most uses of is_i…
ferjm Jan 27, 2019
ea69bbc
Node retargeting algorithm
ferjm Jan 28, 2019
4740ce5
Make note_dirty_descendants jump around shadow roots
ferjm Jan 28, 2019
df81deb
Set connected flag not only on elements
ferjm Jan 28, 2019
1b03635
Bind/unbind shadow host children to/from tree
ferjm Jan 28, 2019
c48ad0f
Introduce concept of composed parent node
ferjm Jan 29, 2019
d6ddb08
Do not care about shadow roots when getting root element
ferjm Jan 29, 2019
6a85409
Throw NotSupported when trying to deep clone a shadow root
ferjm Jan 29, 2019
f6ba165
Throw when trying to import or adopt a shadow root
ferjm Jan 29, 2019
2e5c058
Implement concept of shadow including tree order
ferjm Jan 30, 2019
be06f1e
Always get browsing context from document
ferjm Jan 30, 2019
7c9e8aa
First bits of shadow dom layout
ferjm Jan 31, 2019
d2e1a3a
Adapt traversals for shadow dom
ferjm Feb 1, 2019
0d6bd24
Move stylesheets related code to DocumentOrShadowRoot
ferjm Feb 4, 2019
3bb50cc
ShadowRoot stylesheet list
ferjm Feb 5, 2019
23b92d5
Remove stylesheets ownership from DocumentOrShadowRoot
ferjm Feb 8, 2019
e9f0e76
Implement TShadowRoot::style_data
ferjm Feb 11, 2019
cd07574
Expose a way to flush shadow root stylesheets from layout
ferjm Feb 12, 2019
18c1b8f
Register/unregister shadow roots in documents when they are connected
ferjm Feb 12, 2019
519cc2c
Invalidate and flush shadow tree stylesheets where needed
ferjm Feb 13, 2019
47872cd
Do not add shadow tree styles to stylist
ferjm Feb 13, 2019
8641866
Fix Document.Element(s)FromPoint
ferjm Feb 14, 2019
07e2f41
Retarget result of shadowRoot.element(s)FromPoint
ferjm Feb 14, 2019
efce282
Return composed parent node. Fixes style sharing panics
ferjm Feb 18, 2019
f097233
Update test expectations and whitelist ShadowRoot interface
ferjm Feb 18, 2019
067acdf
Update size of tests with shadow root size
ferjm Feb 19, 2019
2515966
Fix formatting issues
ferjm Feb 19, 2019
0d2f65b
Shadow DOM layout and style reftest
ferjm Feb 19, 2019
e66438d
Fix the way the IS_CONNECTED flag is set
ferjm Feb 19, 2019
d7b6a6f
Do not set dirty out-of-doc nodes
ferjm Feb 19, 2019
39c96ac
Remove IS_CONNECTED flag when node is removed from the doc
ferjm Feb 20, 2019
57fa6b1
Update expectations for cssom tests
ferjm Feb 20, 2019
00178af
Derive PartialEq for ServoShadowRoot
ferjm Feb 20, 2019
a841c71
Minor layout thread cleanups:
ferjm Feb 20, 2019
2674a3e
Flush shadow roots stylesheets only if they changed
ferjm Feb 21, 2019
3ccd622
Introduce ShadowIncluding enum for tree traversals
ferjm Feb 21, 2019
3dd3815
Make StyleSheetListOwner implement JSTraceable
ferjm Feb 21, 2019
d77b9c6
Add invalidate_stylesheets to StyleSheetsListOwner trait
ferjm Feb 21, 2019
67c90a0
Relax attachShadow restrictions for user agent widgets
ferjm Feb 21, 2019
3e63655
Make ServoShadowRoot.flush_stylesheets unsafe
ferjm Feb 28, 2019
3e53962
Do not send RemoveStyleSheet message for shadow roots
ferjm Feb 28, 2019
8b353ee
Make StylesheetSet an enum instead of a trait object
ferjm Mar 1, 2019
2350f0e
Make StyleSheetListOwner an enum instead of a trait object
ferjm Mar 1, 2019
5426996
Derive MallocSizeOf for QuirksMode
ferjm Mar 1, 2019
ea1fe15
Do not store composed parent node
ferjm Mar 4, 2019
ccf8a43
Document owner_shadow_root
ferjm Mar 4, 2019
740aae0
Register named elements in either the document or shadow tree
ferjm Mar 5, 2019
813b242
Introduce BindContext with in_doc and connected flags
ferjm Mar 5, 2019
6af4729
Introduce NodeRareData and ElementRareData
ferjm Mar 6, 2019
a9019da
Move mutation observers list to NodeRareData
ferjm Mar 6, 2019
c75da61
Add custom elements related stuff into ElementRareData
ferjm Mar 6, 2019
1427c43
Update size of tests after *RareData changes
ferjm Mar 6, 2019
b8925a0
Set IS_CONNECTED flag on host and children instead of on containing s…
ferjm Mar 6, 2019
0313e38
Tweak list of shadow roots attached to doc
ferjm Mar 6, 2019
bdd2f32
Minor tweaks: rename composed_parent_node_ref, remove or update outda…
ferjm Mar 7, 2019
5be6779
Revert style/dom_apis changes
ferjm Mar 7, 2019
5a165c6
Move is_connected function from style to layout, where it is used
ferjm Mar 7, 2019
efbfc0f
Make devtools find node by unique id function include shadow trees
ferjm Mar 7, 2019
0ca4792
Revert changes in sheet_set_methods macro
ferjm Mar 7, 2019
8eba587
Merge Node::shadow_including_inclusive_ancestors into inclusive_ances…
ferjm Mar 7, 2019
890297e
Optimize Node::GetRootNode
ferjm Mar 8, 2019
f606963
Update tests manifest
ferjm Mar 8, 2019
6bf1ca2
Make Node and Element rare_data an Option
ferjm Mar 8, 2019
59c634b
Set dirty descendants if node is connected
ferjm Mar 23, 2019
ccc4f7c
Add dom.shadowdom.enabled to preferences list
ferjm Mar 25, 2019
3f312f7
Set dirty descendants flag only for elements
ferjm Mar 26, 2019
9df1c2f
Revert unnecessary format changes
ferjm Apr 22, 2019
e4f34fd
Rename StylesheetSet to StylesheetSetRef
ferjm Apr 22, 2019
9d52fef
Rename shadow_root_from_node to containing_shadow_root
ferjm Apr 22, 2019
d0b2e82
Move StylesheetSetRef to script
ferjm Apr 22, 2019
9b2eb77
Do not lazy initialize RareData on its getters
ferjm Apr 23, 2019
68bee1c
Final nits; fix custom elements rare data usage; s/owner_s_r/containi…
ferjm Apr 26, 2019
37e88e7
Set self as containing_shadow_root for shadow roots
ferjm Apr 29, 2019
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -222,6 +222,9 @@ mod gen {
enabled: bool,
}
},
shadowdom: {
enabled: bool,
},
svg: {
enabled: bool,
},
@@ -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;
}

@@ -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() {
This conversation was marked as resolved by ferjm

This comment has been minimized.

Copy link
@emilio

emilio Feb 20, 2019

Member

This is ok for now. It'd be good to introduce the concept of flat tree in a more formal way, but...

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))
}

@@ -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};
@@ -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());

@@ -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 {
@@ -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;
@@ -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)
})
}
@@ -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;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.